Skip to content

Tableau: Extended Table Creation

I write books and courses, and I need to be able to create tables that are just a bit fancier than regular Markdown can generate.

How place values work in binary

Bit position

7

6

5

4

3

2

1

0

Bits

1

0

0

1

0

0

1

1

Place value

\(2^7\)

\(2^6\)

\(2^5\)

\(2^4\)

\(2^3\)

\(2^2\)

\(2^1\)

\(2^0\)

128

64

32

16

8

4

2

1

Bit x value

128

0

0

16

0

0

2

1

So, I wrote a kind of universal Markdown extension, called Tableau Table. Features include:

  • Headerless tables
  • Multiline headers
  • Multiple separate headers
  • Headers in columns
  • Row and column span
  • Per cell alignment and CSS classes
  • Default attributes, both down columns and across rows
  • Table-wide classes
  • Caption
  • Continuation lines
  • Compatible (with two exceptions) with standard markdown tables

And, because I like to experiment with different layouts and formatting, the tables generated by tableau table are styled using CSS classes, not inline attributes.

If You Want to Use Tableau Tables

You'll need to find an extension/plugin for the particular Markdown processor you're using.

If you use a different Markdown Processor

I use multiple Markdown processors, and I didn't want to have to implement a whole new tableau plugin for each.

Instead, I wrote two library implementations of tableau, one in Python and the other in JavaScript.

These libraries are intended to make it easy to write a plugin so that you can use tableau-formatted tables in a Markdown processor that doesn't currently support it.

I wrote the Marked extension in a couple of hours (most of which was spent fighting with Typescript types).

If you're interested in writing an extension, see the guide. If your particular environment requires features not currently in the library, let me know and I'll add them.

If you'd like to create a version of the libraries for another programming language, please let me know so we can coordinate effort.

Basic Markup

Tableau is compatible with existing Markdown table conventions providing every row in the markup starts and ends with a pipe character:

| Country | Region       | Minimum Nos. | Probably Nos. | Speculative Nos. |
|:--------|:-------------|-------------:|--------------:|-----------------:|
| India   | Northern     |          750 |           875 |            1,000 |
| India   | Northeastern |        7,200 |         9,250 |           11,300 |
| Nepal   |              |           41 |            50 |               60 |

Columns are separated using pipe characters ("|"), and the second line both separates the headings from the table body and also gives the alignment of each of the columns.

Formatted, it looks like this:

Country

Region

Minimum Nos.

Probably Nos.

Speculative Nos.

India

Northern

750

875

1,000

India

Northeastern

7,200

9,250

11,300

Nepal

41

50

60

Multiple Header Lines

Country

Region

Minimum

Probable

Speculative

Numbers

Numbers

Numbers

India

Northern

750

875

1,000

India

Northeastern

7,200

9,250

11,300

Nepal

41

50

60

| Country | Region       | Minimum  | Probable | Speculative  |
|         |              | Numbers  | Numbers  | Numbers      |
|:--------|:-------------|---------:|---------:|-------------:|
| India   | Northern     |      750 |      875 |        1,000 |
| India   | Northeastern |    7,200 |    9,250 |       11,300 |
| Nepal   |              |       41 |       50 |           60 |

Every line before the format line is considered a header.

No Header Lines

India

Northern

750

875

1,000

India

Northeastern

7,200

9,250

11,300

Nepal

41

50

60

|:--------|:-------------|---------:|---------:|-------------:|
| India   | Northern     |      750 |      875 |        1,000 |
| India   | Northeastern |    7,200 |    9,250 |       11,300 |
| Nepal   |              |       41 |       50 |           60 |

No Format Line

India

Northern

750

875

1,000

India

Northeastern

7,200

9,250

11,300

Nepal

41

50

60

| India   | Northern     |      750 |      875 |        1,000 |
| India   | Northeastern |    7,200 |    9,250 |       11,300 |
| Nepal   |              |       41 |       50 |           60 |

Spanned Rows

Country

Region

Minimum

Probable

Speculative

Numbers

Numbers

Numbers

India

Northern

750

875

1,000

India

Northeastern

7,200

9,250

11,300

Nepal

41

50

60

| Country | Region       | Minimum  | Probable | Speculative  |
|^        |^             | Numbers  | Numbers  | Numbers      |
|:--------|:-------------|---------:|---------:|-------------:|
| India   | Northern     |      750 |      875 |        1,000 |
| India   | Northeastern |    7,200 |    9,250 |       11,300 |
| Nepal   |              |       41 |       50 |           60 |

An up-arrow (^) immediately after the pipe in any cell causes it to merge with the cell above. Multiple consecutive cells can be merged.

Tableau Format Specifications

In the preceding table, the two cells at the start of the second row are merged with the cells above. This is done using the up-arrow format specification.

Every cell in a tableau table can have one or more format specifications. They must immediately follow the cell's opening pipe, with no intervening spaces.

The valid format specifications are:

^

Merge this cell with the one above it. Multiple cells in a column may be merged

{

Merge this cell with the one to its left. Multiple cells in a row may be merged

<

Left align this cell

=

Center this cell

>

Right align this cell

#

Make this cell a header (use <th>)

.class

Apply the CSS class to this cell

The two span specifications (^ and {) must appear alone (as the cell formatting is taken from the cell above or to the left). Otherwise, a format specification can contain an optional alignment specifier, an optional heading specifier, and zero or more class specifiers. These three types of specifier can appear in any order:

|<#.warn.big Note |.info> Meaning |

The first cell is a left-aligned heading cell with the CSS classes warn and big, while the second is a right-aligned cell with the class info.

Propagating Formats Along a Row

Any format specification can be followed by an ellipsis (either three periods ... or a Unicode ellipsis, ). This format will then be propagated as a default to subsequent cells in the same row.

For example, a tableau header row can be created without propagating the # format using:

|# Country |# Region       |# Minimum Nos. |# Probably Nos. |# Speculative Nos. |
| ...      |

Country

Region

Minimum Nos.

Probably Nos.

Speculative Nos.

...

You can also use the heading specifier on just the first column, and use an ellipsis to propagate it.

|#… Country |Region       |Minimum Nos. |Probably Nos. |Speculative Nos. |
| ...      |

Country

Region

Minimum Nos.

Probably Nos.

Speculative Nos.

...

The propagated format is just a default: it can be overridden in any subsequent cell.

|!.vlines |
|#... Animal 1 | Animal 1 | Animal 1 | Animal 1 | Animal 1 |
|>... ant | bee |<cat | dog | elk |

Animal 1

Animal 1

Animal 1

Animal 1

Animal 1

ant

bee

cat

dog

elk

You can even override it in the cell that initially propagates it:

|#...>... Animal 1 | Animal 1 | Animal 1 | Animal 1 | Animal 1 |
|>...< ant | bee |<cat | dog | elk |

Animal 1

Animal 1

Animal 1

Animal 1

Animal 1

ant

bee

cat

dog

elk

Propagating Formats Down a Column

Any tableau row that starts |: is a format row. We've already seen a version of this:

| Country | Region       | Minimum Nos. | Probably Nos. | Speculative Nos. |
|:--------|:-------------|-------------:|--------------:|-----------------:|
| India   | Northern     |          750 |           875 |            1,000 |
| India   | Northeastern |        7,200 |         9,250 |           11,300 |
| Nepal   |              |           41 |            50 |               60 |

The :--- specifier in the first cell of line two is conventional markdown for "subsequent cells in this column should be left aligned".

You can specify the same thing using tableau format specifiers:

|#... Country | Region       | Minimum Nos. | Probably Nos. | Speculative Nos. |
|:<           |<             |>...          |               |                  |
| India       | Northern     |          750 |           875 |            1,000 |
| India       | Northeastern |        7,200 |         9,250 |           11,300 |
| Nepal       |              |           41 |            50 |               60 |

Country

Region

Minimum Nos.

Probably Nos.

Speculative Nos.

India

Northern

750

875

1,000

India

Northeastern

7,200

9,250

11,300

Nepal

41

50

60

By moving the format line above the header, you can align that header, too:

|:<           |<             |>...          |               |                  |
|#... Country | Region       | Minimum Nos. | Probably Nos. | Speculative Nos. |
| India       | Northern     |          750 |           875 |            1,000 |
| India       | Northeastern |        7,200 |         9,250 |           11,300 |
| Nepal       |              |           41 |            50 |               60 |

Country

Region

Minimum Nos.

Probably Nos.

Speculative Nos.

India

Northern

750

875

1,000

India

Northeastern

7,200

9,250

11,300

Nepal

41

50

60

Here's another example that makes both the first row and first column into headers.

|: #<            | >     | >     | >     | >     | >     | >     | >     | >     |
|#… Bit position | 7     | 6     | 5     | 4     | 3     | 2     | 1     | 0     |
| Bits           | 1     | 0     | 0     | 1     | 0     | 0     | 1     | 1     |
| Place value    | $2^7$ | $2^6$ | $2^5$ | $2^4$ | $2^3$ | $2^2$ | $2^1$ | $2^0$ |
|^               | 128   | 64    | 32    | 16    | 8     | 4     | 2     |     1 |
| Bit x value    | 128   |  0    |  0    | 16    | 0     | 0     | 2     |     1 |

Bit position

7

6

5

4

3

2

1

0

Bits

1

0

0

1

0

0

1

1

Place value

\(2^7\)

\(2^6\)

\(2^5\)

\(2^4\)

\(2^3\)

\(2^2\)

\(2^1\)

\(2^0\)

128

64

32

16

8

4

2

1

Bit x value

128

0

0

16

0

0

2

1

Table Level Formatting

A line starting |! can be used to add CSS classes and/or a caption to the table.

|!.hlines.vlines.wide How place values work in binary |
|: #<            | >     | >     | >     | >     | >     | >     | >     | >     |
|#… Bit position | 7     | 6     | 5     | 4     | 3     | 2     | 1     | 0     |
| Bits           | 1     | 0     | 0     | 1     | 0     | 0     | 1     | 1     |
| Place value    | $2^7$ | $2^6$ | $2^5$ | $2^4$ | $2^3$ | $2^2$ | $2^1$ | $2^0$ |
|^               | 128   | 64    | 32    | 16    | 8     | 4     | 2     |     1 |
| Bit x value    | 128   |  0    |  0    | 16    | 0     | 0     | 2     |     1 |

The three classes add horizontal and vertical lines to the table and force it to be a full column wide. Any text after the format becomes the table caption.

How place values work in binary

Bit position

7

6

5

4

3

2

1

0

Bits

1

0

0

1

0

0

1

1

Place value

\(2^7\)

\(2^6\)

\(2^5\)

\(2^4\)

\(2^3\)

\(2^2\)

\(2^1\)

\(2^0\)

128

64

32

16

8

4

2

1

Bit x value

128

0

0

16

0

0

2

1

Another example of table-level classes is the list of format specifications, which uses the CSS stripe class to get alternating row backgrounds.

Nutrition Facts

One more example, showing a mockup of a nutrition facts panel. We use a CSS style to make the first and last columns bold (starting at the "Calories 200" line).

We use three more styles, ull, ulm, and ulb, to draw light, medium, or bold lines under rows, and we use colspans both to indent subcategories and to control where lines are drawn.

Finally we use a table formatting line (the last line in the table) to both set a caption and to give the table a CSS class. We use that class both to draw a box around the table and to namespace our other styles. This line also shows the use of a backslash to continue a table row onto the next line.

Here's the table markup:

|:< |< |> |
|.ulb…  Serving Size 1/2 cup (about 82g)<br/>Servings Per Container 8 |{ |{ |
|.ull… Amount Per Serving |{ |{ |
|:.bold  | |.bold|
|.ulm… Calories 200 |{ | Calories from Fat 130 |
|.ull… | | % Daily Value |
| Total Fat  |{              | 22% |
|  |.ull… Saturated Fat 9g   | 22% |
|.ull…  | Trans Fat 0g       |  0% |
|.ull… Cholesterol 55mg  |{  | 18% |
|.ull… Sodium 40mg |{        |  2% |
| Total Carbohydrate 17g |{  |  6% |
|  |.ull… Dietary Fiber 1g   |  4% |
|.ull…  | Sugars 14g         |  0% |
|.ulm… Protein 3g |{ |
|!.nutrition (from \
   [examples of data tables](https://wpdatatables.com/examples-of-data-tables/)) |

The CSS styles are included inline in the Markdown:

<style>
table.nutrition {
  border: solid 6px #666;
}
table.nutrition .bold {
    font-weight: bold;
}

table.nutrition .ull {
    border-bottom: 1px solid #222;
}

table.nutrition .ulm {
    border-bottom: 2px solid #333;
}

table.nutrition .ulb {
    border-bottom: 8px solid #666;
}
</style>

And the result is:

(from examples of data tables)

Serving Size 1/2 cup (about 82g)
Servings Per Container 8

Amount Per Serving

Calories 200

Calories from Fat 130

% Daily Value

Total Fat

22%

Saturated Fat 9g

22%

Trans Fat 0g

0%

Cholesterol 55mg

18%

Sodium 40mg

2%

Total Carbohydrate 17g

6%

Dietary Fiber 1g

4%

Sugars 14g

0%

Protein 3g

Formatting and CSS

Unlike most markdown table formatters, tableau uses CSS classes and not inline markup to format tables. The stylesheet I used to format this document can be found in docs/assets/css/tableau (and a version using Postcss' preset-env can be found in the project's css directory.)