An HTML table generator prompt that sorts right and edits in place

Tables are the case where generated output looks right and behaves wrong. Text sorting applied to numbers only shows up when you click a column.

Using an assistant as an HTML table generator gets you a table that looks right and behaves wrong, because the default sort compares numbers as text and 100 lands before 9.

What the HTML table generator prompt produces: columns that sort as numbers, cells that edit in place.
What the HTML table generator prompt produces: columns that sort as numbers, cells that edit in place.

This prompt produces one self-contained file whose columns sort numerically, whose cells edit in place, and which reads on a phone.

This guide gives the prompt, the reasoning for each part, the six checks to run after it renders, and the four steps to share the table so more than one person is looking at the same rows.

Tables are the case where generated output looks right and behaves wrong, because the flaw — text sorting applied to numbers — only shows up when you click a column.

The HTML table generator prompt

Build one complete, self-contained HTML file containing a data table.

STRUCTURE
- doctype, head with charset, viewport, descriptive title
- all CSS in one <style> tag; all JS in one <script> before </body>
- no libraries
- proper table markup: thead, tbody, th scope="col"

SORTING
- clicking a column header sorts by that column, toggling direction
- numeric columns must sort numerically: strip non-digits, parseFloat,
  fall back to localeCompare for text
- set aria-sort on the active header

EDITING
- every td is contenteditable="true"
- a visible focus style on td:focus
- save rows to localStorage on input, wrapped in try/catch
- an "Add row" button and a "Download CSV" button

LAYOUT
- wrap the table in a div with overflow-x: auto; table min-width 560px
- numeric columns: text-align right, font-variant-numeric: tabular-nums
- header row tinted, hairline bottom borders only, no vertical rules
- long cells vertical-align: top

Why each part is there

"Numeric columns must sort numerically"

The default comparison is textual, which produces this:

1
10
100
9

And with formatting it gets worse — $1,284 sorts before $998 because 1 precedes 9 as a character. The fix:

<script>
  var nx = parseFloat(x.replace(/[^0-9.-]/g, ''));
  var ny = parseFloat(y.replace(/[^0-9.-]/g, ''));
  var numeric = x && y && !isNaN(nx) && !isNaN(ny);
  return numeric ? nx - ny : x.localeCompare(y);
</script>

Stripping non-digits before parsing is what makes $1,284 and 41 min sort as numbers. localeCompare rather than < is what puts accented names in the right place.

"aria-sort on the active header"

One attribute, and the sorted state is announced rather than merely shown by an arrow. Costs nothing.

"A visible focus style on td:focus"

td:focus { outline: 2px solid #d5f525; outline-offset: -2px; background: #fcffe8; }

Without this, clicking a cell produces no visible change and nobody discovers the table can be edited. The feature exists and is invisible.

"try/catch around localStorage"

Storage throws rather than failing quietly in two ordinary situations: private browsing, and pages rendered inside a restricted frame. Unguarded, that exception halts the rest of the script — so the table becomes unsortable as well as unsaveable. See local storage.

"overflow-x: auto wrapper"

Six columns cannot be squashed into a phone screen and stay readable. Let the table scroll inside its own box:

.scroller { overflow-x: auto; -webkit-overflow-scrolling: touch; }
table { min-width: 560px; }

The rest of the page stays still. Two lines.

"Proper table markup"

thead, tbody and scope="col" are what let a screen reader announce "Owner, Jae" rather than reading a stream of unlabelled words. The same rows built from styled divs look no different on screen and are useless to anyone not looking at it. See semantic HTML.

"Download CSV"

One small function, and the table stops being a dead end for anyone who needs the data in a spreadsheet:

<script>
  var v = cell.textContent.replace(/"/g, '""');
  out.push(/[",\n]/.test(v) ? '"' + v + '"' : v);
</script>

Quoting any value containing a comma, quote or newline is what stops the export breaking on the first free-text cell.

Changing the prompt for your own table

Name the columns and say which are numeric, dates or text; the comparator handles the first two the same way but the assistant will format dates better if it knows. Say roughly how many rows to expect.

Past a hundred, ask for a filter box and a sticky header; past a thousand, a table is the wrong tool and you want a page per group.

Leave the SORTING and EDITING blocks exactly as written, because those are the lines that fail silently without instruction.

The limit worth knowing

Local storage belongs to one browser on one machine. Send the file to four people and you get four divergent tables that cannot see each other, with no way to merge them and no way to tell which is current.

A copy per person ✗ Each edit lives on one machine ✗ No way to merge the changes ✗ Nobody can say which is current ✗ The oldest copy keeps circulating One address ✓ Everyone opens the same page ✓ A correction is seen by all ✓ There is only one current version ✓ Forwarding shares the page, not a copy
A copy per reader versus one address.

If more than one person needs the same rows, the table has to live at one address. Publish the page — in NOS the pasted table renders exactly as written, its cells hold clickable text, and everyone on the link is looking at the same rows.

Or start from the table builder, which produces this file already assembled.

What to check after it renders

Check What a failure looks like
Click a numeric column header 100 sorts before 9 — text comparison
Click a cell No visible change — no focus style
Type, then reload Edits gone — no storage, or storage threw
Narrow the window to 360px Columns squashed instead of scrolling
Tab through the headers Cannot reach them — not focusable
Use a screen reader on a row Unlabelled stream — missing scope="col"
The six checks. The first and third are the ones generated tables fail most.
The six checks. The first and third are the ones generated tables fail most.

Six checks, a minute in total. The first and third are the ones generated tables fail most often.

The storage answer, stated plainly

A generated table can save to the local browser, and that is per machine — so four recipients produce four divergent tables. See localStorage for why, including the exception it throws inside a preview frame.

If more than one person needs the same rows, the table has to live at one address. Or start from the table builder, which produces the file with all six checks above already handled.

From the prompt to a shared table: 4 steps

  1. Paste the prompt and name your columns. Keep the SORTING, EDITING and LAYOUT blocks as written. Say which columns are numeric or dates.
  2. Run the six checks. Sort a numeric column, click a cell, type and reload, narrow to 360px, tab through the headers, read a row with a screen reader. The first and third are the ones generated tables fail most.
  3. Give the table an address and copy it. Share, then Share link, then Create link. One set of rows for everyone, and one place to fix a number. Turning HTML into a link is this step.
  4. Correct rows on the page, not in copies. Click the cell, type over it. The link everyone has shows the corrected row, and there is no spreadsheet v3 in anyone's inbox.
Share the table at an address so more than one person is looking at the same rows.
Share the table at an address so more than one person is looking at the same rows.

Questions people ask

Why do generated tables sort incorrectly?

Because the default comparison is textual, which puts 100 before 9 and $1,284 before $998. Numeric sorting has to be asked for explicitly.

How do I make cells editable?

contenteditable="true" on each td. Ask for it, plus a visible focus style — without one, nobody discovers the table is editable.

What about phone width?

Ask for a wrapper with overflow-x: auto and a min-width on the table. It scrolls sideways inside its own box rather than squashing the columns.

Where do edits go?

Nowhere, unless something stores them. In a standalone file the best available is the local browser, which means each reader gets their own divergent copy.

Keep reading