ul-table: Markdown Tables Without New Syntax

ul-table is an HTML processor that lets you write tables as bulleted lists, in Markdown.

Table of Contents
Simple Example
About ul-table
Why?
Structure
Markdown → HTML → HTML Conversion
Details
Comparison: Tedious Inline HTML
Stylesheet
Adding HTML Attributes
Cells
Columns
Rows
Example: Markdown and HTML Inside Cells
Markdown Quirks to Be Aware Of
Comparisons
CommonMark Doesn't Have Tables
Github Tables are Awkward
MediaWiki Tables
Conclusion
Related Docs
Appendix: Implemention
Algorithm Notes
Appendix: Real Examples
HTML Quirks
Ideas for Features

Simple Example

To make this table:

Shell Version
bash 5.2
OSH 0.25.0

You write:

<table>

- thead
  - Shell
  - Version
- tr
  - [bash](https://www.gnu.org/software/bash/)
  - 5.2
- tr
  - [OSH](https://oils.pub/)
  - 0.25.0

</table>

Any Markdown processor will produce this:

And then our ul-table plugin transforms that into the table shown.

So the conversion takes 2 steps. The intermediate form is what sourcehut or Github will show, because they currently don't support ul-table.

This is good, because it means that ul-table degrades gracefully! You can use it anywhere without worrying about breakage.

About ul-table

Why?

Because it's tedious to read, write, and edit <tr> and <td> and </td> and </tr>. Aligning columns is also tedious in HTML.

Design goals:

Structure

You make tables with a two-level Markdown list, between <table> tags. The top level list contains either:

thead zero or one, at the beginning
tr zero or more, after thead

The second level contains the contents of cells, but you don't write td or <td>.

(This format looks similar to tables in reStructuredText).

Markdown → HTML → HTML Conversion

As mentioned, it takes two steps to convert:

  1. Any Markdown translator will produce a <table> <ul> <li> ... </li> </ul> </table> structure.
  2. Our ul-table plugin transforms that into a <table> <tr> <td> </td> </tr> </table> structure, which is a normal HTML table.

So ul-table is an HTML processor, not a Markdown processor. But it's meant to be used with Markdown.

Details

Comparison: Tedious Inline HTML

Here's the equivalent in CommonMark:

<table>
  <thead>
    <tr>
      <td>Shell</td>
      <td>Version</td>
    </tr>
  </thead>
  <tr>
    <td>

<!-- be careful not to indent this 4 spaces! -->
[bash](https://www.gnu.org/software/bash/)

    </td>
    <td>5.2</td>
  </tr>
  <tr>
    <td>

[OSH](https://oils.pub/)

    </td>
    <td>0.25.0</td>
  </tr>

</table>

It uses the rule where you can embed Markdown inside HTML inside Markdown. With ul-table, you don't need this mutual nesting.

The ul-table text is also shorter!


Trivia: with CommonMark, you get an extra <p> element:

<td>
  <p>OSH</p>
</td>

ul-table can produce simpler HTML:

<td>
  OSH
</td>

Stylesheet

To make the table look nice, I add a <style> tag, inside Markdown:

<style>
table {
  margin: 0 auto;
}
td {
  padding-left: 1em;
  padding-right: 1em;
}
</style>

Adding HTML Attributes

HTML attributes like <tr class=foo> and <td id=bar> let you format and style your table.

You can add attributes to cells, columns, and rows.

Cells

Name Age
Alice 42
Bob 9

Add cell attributes with a cell-attrs tag after the cell contents:

- thead
  - Name
  - Age
- tr
  - Alice
  - 42 <cell-attrs class=hi />
- tr
  - Bob
  - 9

It's important that cell-attrs is a self-closing tag:

<cell-attrs />  # Yes
<cell-attrs>    # No: this is an opening tag

How does this work? ul-table takes the attributes from <cell-attrs />, and puts it on the generated <td>.

Columns

Name Age
Alice 42
Bob 9

To add attributes to every cell in a column, put <cell-attrs /> in the thead section:

- thead
  - Name
  - Age <cell-attrs class=num /> 
- tr
  - Alice
  - 42     <!-- this cell gets class=num -->
- tr
  - Bob
  - 9      <!-- this cells gets class=num -->

This is particularly useful for aligning numbers to the right:

<style>
.num {
  align: right;
}
</style>

If the same attribute appears in a column in both thead and tr, the values are concatenated, with a space. Example:

<td class="from-thead from-tr">

Rows

Name Age
Alice 42
Bob 9

To add row attributes, put <row-attrs /> after the - tr:

- thead
  - Name
  - Age
- tr
  - Alice
  - 42
- tr <row-attrs class="special-row" />
  - Bob
  - 9

Example: Markdown and HTML Inside Cells

Here's an example that uses more features. Source code of this table: doc/ul-table.md.

Shell Version Example Code
bash 5.2
echo sh=$bash
ls /tmp | wc -l
echo
dash 1.5 Inline HTML
mksh 4.0
HTML table inside
this table no way to re-enter inline markdown though?
zsh 3.6 Unordered List
  • one
  • two
yash 1.0 Ordered List
  1. one
  2. two

ksh

This is paragraph one.

This is paragraph two

Another cell with ...

... multiple paragraphs.

 

Another table:

OSH YSH
my-copy() {
  cp --verbose "$@"
}
proc my-copy {
  cp --verbose @ARGV
}
x y

Markdown Quirks to Be Aware Of

Here are some quirks I ran into when creating ul-tables.

(1) CommonMark doesn't allow empty list items:

- thead
  -
  - above is not rendered as a list item

You can work around this by using a comment, or invisible character:

- tr
  - <!-- empty -->
  - above is OK
- tr
  - &nbsp;
  - also OK

(2) Similarly, a cell with a literal hyphen may need a comment or space in front of it:

- tr
  - <!-- hyphen --> -
  - &nbsp; -

Comparisons

CommonMark Doesn't Have Tables

Related discussions:

Github Tables are Awkward

Github-flavored Markdown has an non-standard extension for tables:

This style is hard to read and write, especially with large tables:

| Command | Description |
| --- | --- |
| git status | List all new or modified files |
| git diff | Show file differences that haven't been staged |

Our style is less noisy, and more easily editable:

<table>

- thead
  - Command
  - Description
- tr
  - git status
  - List all new or modified files
- tr
  - git diff
  - Show file differences that haven't been staged

</table>

MediaWiki Tables

Here is a long page describing how to make tables on Wikipedia:

I created the equivalent of the opening example:

{| class="wikitable"
! Shell !! Version
|-
| [https://www.gnu.org/software/bash/ Bash] || 5.2
|-
| [https://www.oilshell.org/ OSH] || 0.25.0
|}

In general, it has more "ASCII art", and invents a lot of new syntax.

I prefer ul-table because it reuses Markdown and HTML syntax.

Conclusion

ul-table is a nice way of writing and maintaining HTML tables. The appendix has links and details.

Related Docs

Appendix: Implemention

Algorithm Notes

TODO: I would like someone to produce a DOM-based implementation!

Our implementation is pretty low-level. It's meant to avoid the "big load anti-pattern" (allocating too much), so it's a necessarily more verbose.

A DOM-based implementation should be much less than 1000 lines.

Appendix: Real Examples

I converted the tables in these September posts to ul-table:

The markup was much shorter and simpler after conversion!

TODO:

HTML Quirks

Ideas for Features


We could help users edit well-formed tables with enforced column names:

- thead
  - <cell-attrs ult-name=name /> Name
  - <cell-attrs ult-name=age /> Age
- tr 
  - <cell-attrs ult-name=name /> Hi
  - <cell-attrs ult-name=age /> 5

This is a bit verbose, but may be worth it for large tables.

Less verbose syntax idea:

- thead
  - <ult col=NAME /> <cell-attrs class=foo /> Name
  - <ult col=AGE /> Age
- tr 
  - <ult col=NAME /> Hi
  - <ult col=AGE /> 5

Even less verbose:

- thead
  - {NAME} Name
  - {AGE} Age
- tr 
  - {NAME} Hi
  - {AGE} 5

The obvious problem is that we might want the literal text {NAME} in the header. It's unlikely, but possible.

Generated on Fri, 27 Dec 2024 03:51:45 +0000