It would be cool if <table> tags had a spiffy default style

I’m continuing to putter away in my baseball notebook.

As I integrate more data, I’m experimenting with surfacing some of it ina table. Your excellent templating tricks make that easy, but the default table style is pretty plain. I doubt I’ve thought through all of the implications, but in in this case I would appreciated an “opinionated” bit of CSS that doctored up the table to fit your site style.

I dunno, Ben. Our default table style looks pretty reasonable to me:

Here’s all the current CSS:

table {
  width: 100%;
  max-width: 640px;
  border-spacing: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: 14px;

th {
  text-align: left;

thead th {
  border-bottom: solid 1px #ccc;

tbody tr:not(:last-child) td {
  border-bottom: solid 1px #eee;

What sort of spiffiness are you looking for? I don’t think we’re going to go as far as zebra striping, or hover highlighting cells, or anything with that level of zazz…


Ah! I didn’t realize that thead and tbody would bring some styles with them. Very good!

I think we could really use some great table defaults with real sophisticated professional defaults - like I’d really love to

  • automatically use tabular numbers, so numbers line up in columns
  • have good defaults for right-aligning numeric rows
  • and good defaults for rows with decimal accuracy
  • and maybe even table sorting

But after tinkering with implementing this in Observable-the-application-itself, I think it’ll really be best as a notebook that people can import, since even though these table tweaks are great, they’re also a bit opinionated, and having this be opt-in avoids the case where someone just wants tables that are more or less the HTML defaults. Our built-in styles are pretty light-touch and they should probably stay that way, to maximize flexibility.


Okay! After putting this on the backburner for a week, I gave it a first shot:

Reusable tables. Hits many of my initial goals:

  • Decent, minimal stylistic defaults
  • Built-in support for rank columns, sorting
  • Light-touch auto-styling for numeric columns
  • Custom cell formatters
  • Customizable titles

Feedback welcome! Would love to hit 98% of the common cases with this notebook and make it simple to drop in better tables for analysis.


I’ll give it a try. I am sympathetic to @jashkenas fear of going overboard. Though, as I’ve said in other threads here, I do think the RMarkdown table filters and one of its few ++ features that its competitors lack.

Here’s an example of that

1 Like

I’ve built something similar as well:


Hi Tom, I was playing with your notebook and noticed some minor problems with the paging. Here is a possible fix. Spotify fetch also seems broken currently. Thanks for the notebook. I learnt some good techniques to make things work in Observable (i.e. modifying the cell html with rerender).

       ${pages ? html`<div class='pretty-pager'>
          <button data-action="previous" ${page==0 ? 'disabled' : ''}>Previous</button>
          ${Array.from({ length: pages }).map((_, i) =>
            html`<button data-page="${i}">${i + 1}</button>`
          <button data-action="next" ${page==pages-1 ? 'disabled' : ''}>Next</button>
        </div>` : ''}
1 Like

Yep, you’re right - I’ve been meaning to take some time and really improve the table output. Will do so soon.

offtopic: @gampleman, I don’t know if this was on purpose but I love how it makes the page look like it’s breathing:


Great work @tom! How hard would it be to be able to add a search box that would filter rows from a table (in a observable fashion)? I’m working on a table viewer for the latest COVID-19 Biorxiv publications which can be used to pull up abstracts and links to papers. However, it would be cool to be able to filter out papers using a search box.

I just added a search feature by setting a variable - will switch to better looking search box later.

Hello @tmcw, when the number of elements in the table are less than the number of elements per page perhaps someone would like the Previous 1 Next links removed. One liner change here:

Relevant change:

pages && data.length > paged

1 Like

Hi @tom! I’m a big fan of your tables. Thanks so much for your work! The two features that I’m really interested in from your tables are the sortable columns and the search function, but it doesn’t look like both are available in the same iteration. Do you know of a way to add searchability to these tables ( or conversely sortability to these tables (



I added a search box to the first iteration of @tom’s table by following @cornhundred’s example, which filters data based on search strings like so:

filtered_data = merged_data.filter(x => {
  let search_string =
    x.Title.toString().toLowerCase() + x.Author.toString().toLowerCase();
  return search_string.includes(inst_search.toLowerCase());

Here’s how I’m implementing it in my own notebook.