ul-table: Markdown Tables Without New Syntax

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

It's a short program I wrote because I got tired of reading and writing <tr> and <td> and </td> and </tr>. And I got tired of aligning numbers by writing <td class="num"> for every cell.

Table of Contents
Simple Example
Design
Goals
Comparison
Details
ul-table "Grammar"
Stylesheet
Adding HTML Attributes
Cells
Columns
Rows
More Complex Example
Markdown Quirks
Conclusion
Related Docs
Appendix: Implemention
Notes on the Algorithm
Appendix: Real Examples
HTML Quirks
Ideas for Features

Simple Example

Let's see how it works. How do you make this table?

Shell Version
bash 5.2
OSH 0.25.0

With ul-table, you create a two-level Markdown list, inside <table> tags:

<table>

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

</table>

The header and data rows are at the top level, and the cells are indented under them.


The conversion takes 2 steps: it's Markdown → HTML → HTML.

First, any Markdown processor will produce this list structure, with <ul> and <li>:

Second, our ul-table plugin parses and transforms that into a table, with <tr> and <td>:

Shell Version
bash 5.2
OSH 0.25.0

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

Design

Goals

Comparison

Compared to other table markup formats, ul-table is shorter, less noisy, and easier to edit:

Details

ul-table "Grammar"

Recall that a ul-table is 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>.

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

You must use a self-closing tag:

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

Notice that ul-table takes the attributes from the <cell-attrs /> tag, and puts it on the generated <td> tag.

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 -->

Then every <td> in the column will "inherit" those attributes. This is 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

More Complex Example

This example uses more features, like Markdown and HTML inside cells. You may want to view the source text for 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

Here are some quirks I ran into when using ul-table.

(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; -

Conclusion

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

Related Docs

Appendix: Implemention

Notes on the Algorithm

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 16:21:38 -0500