Warm Editorial — data
Warm hairline rules, an uppercase header, sage and bronze status pills. Below 720px each row turns into a card and every cell carries its own label, so nothing is lost to a horizontal scrollbar.
Paste this into Claude, Cursor or Copilot — or point the agent at
https://ui-deploy.com/r/warm-editorial/data-table/prompt.md
# Schedule Table — Warm Editorial
Paste this whole file into Claude, Cursor or Copilot.
---
## What to build
A quiet data table inside a rounded 28px panel. Uppercase header row on the raised
surface, warm hairline rules between rows, and status shown as a small pill.
**Below 720px the table stops being a table.** Each row becomes a card and every
cell prints its own label from `data-label`.
---
## Tokens
```css
:root{
--bg2:#F6F3ED; --bg3:#E4DED2; --border:#DAD3C6;
--text:#22201D; --text2:#5C574F; --text3:#8C857A;
--accent-text:#7D6644; --accent-s:rgba(163,138,106,.12);
--accent-b:rgba(163,138,106,.30);
--sage:#7C8A76; --sage-s:rgba(124,138,118,.12);
--radius:28px; --radius-pill:999px;
--font-display:'Inter Tight',system-ui,sans-serif;
--font-body:'Inter',system-ui,sans-serif;
}
[data-theme="dark"]{
--bg2:#1E1A17; --bg3:#262119; --border:#2E2822;
--text:#EDE7DD; --text2:#A79E92; --text3:#8A8177;
--accent-text:#C0A888; --accent-s:rgba(179,155,124,.13);
--accent-b:rgba(179,155,124,.28);
--sage:#93A08C; --sage-s:rgba(147,160,140,.13);
}
```
---
## Rules — these are not preferences
1. **Do not solve mobile with `overflow-x:auto`.** A sideways-scrolling table hides
its right-hand columns, and the right-hand columns are where the answer usually
is. Restack into cards instead.
2. **Hide the `<thead>` visually, not with `display:none`.** Use the clip pattern so
screen readers still announce the column names.
3. **Every cell needs `data-label`** matching its column, or the mobile card layout
loses its meaning.
4. **Status is stated in words** — "Full", "Waitlist", "4 open". Colour confirms the
message; it is never the message.
5. **Sage is the second voice.** Use it for positive status and small marks only.
Bronze stays the single accent that speaks.
6. **Headers take `scope="col"` and the table takes a `<caption>`.** The caption
doubles as the section eyebrow here — styled uppercase, wide-tracked, bronze.
7. Row rules are `--border` hairlines. No zebra striping; it fights the warm palette.
8. The panel corner is 28px with `overflow:hidden` so the header corners clip.
---
## Adapting it
- **Sortable columns** — put a real `<button>` inside the `<th>` and set
`aria-sort` on the header. Do not make the whole `<th>` clickable.
- **Many rows** — paginate. This table is designed to be read, not scrolled; past
about 15 rows the language stops working and you want a denser one.
- **Numeric columns** — right-align and use `font-variant-numeric:tabular-nums`.
---
## Reference implementation
See `code.html`. No JavaScript.
Paste this whole file into Claude, Cursor or Copilot.
A quiet data table inside a rounded 28px panel. Uppercase header row on the raised surface, warm hairline rules between rows, and status shown as a small pill.
Below 720px the table stops being a table. Each row becomes a card and every cell prints its own label from data-label.
:root{
--bg2:#F6F3ED; --bg3:#E4DED2; --border:#DAD3C6;
--text:#22201D; --text2:#5C574F; --text3:#8C857A;
--accent-text:#7D6644; --accent-s:rgba(163,138,106,.12);
--accent-b:rgba(163,138,106,.30);
--sage:#7C8A76; --sage-s:rgba(124,138,118,.12);
--radius:28px; --radius-pill:999px;
--font-display:'Inter Tight',system-ui,sans-serif;
--font-body:'Inter',system-ui,sans-serif;
}
[data-theme="dark"]{
--bg2:#1E1A17; --bg3:#262119; --border:#2E2822;
--text:#EDE7DD; --text2:#A79E92; --text3:#8A8177;
--accent-text:#C0A888; --accent-s:rgba(179,155,124,.13);
--accent-b:rgba(179,155,124,.28);
--sage:#93A08C; --sage-s:rgba(147,160,140,.13);
}
overflow-x:auto. A sideways-scrolling table hides its right-hand columns, and the right-hand columns are where the answer usually is. Restack into cards instead.<thead> visually, not with display:none. Use the clip pattern so screen readers still announce the column names.data-label matching its column, or the mobile card layout loses its meaning.scope="col" and the table takes a <caption>. The caption doubles as the section eyebrow here — styled uppercase, wide-tracked, bronze.--border hairlines. No zebra striping; it fights the warm palette.overflow:hidden so the header corners clip.<button> inside the <th> and set aria-sort on the header. Do not make the whole <th> clickable.
about 15 rows the language stops working and you want a denser one.
font-variant-numeric:tabular-nums.See code.html. No JavaScript.
<!-- Warm Editorial — Schedule table
Requires tokens.css. Below 720px each row becomes a labelled card. -->
<div class="we-table-wrap">
<table class="we-table">
<caption class="we-table-cap">This week at the studio</caption>
<thead>
<tr>
<th scope="col">Session</th>
<th scope="col">Day</th>
<th scope="col">Duration</th>
<th scope="col">Places</th>
</tr>
</thead>
<tbody>
<tr>
<td data-label="Session"><span class="we-td-strong">Contrast therapy</span></td>
<td data-label="Day">Monday, 07:00</td>
<td data-label="Duration">90 min</td>
<td data-label="Places"><span class="we-pill we-pill-open">4 open</span></td>
</tr>
<tr>
<td data-label="Session"><span class="we-td-strong">Breath downshift</span></td>
<td data-label="Day">Tuesday, 18:30</td>
<td data-label="Duration">45 min</td>
<td data-label="Places"><span class="we-pill we-pill-open">2 open</span></td>
</tr>
<tr>
<td data-label="Session"><span class="we-td-strong">Assessment</span></td>
<td data-label="Day">Thursday, 09:00</td>
<td data-label="Duration">90 min</td>
<td data-label="Places"><span class="we-pill we-pill-wait">Waitlist</span></td>
</tr>
<tr>
<td data-label="Session"><span class="we-td-strong">Sustained recovery</span></td>
<td data-label="Day">Saturday, 10:00</td>
<td data-label="Duration">120 min</td>
<td data-label="Places"><span class="we-pill we-pill-full">Full</span></td>
</tr>
</tbody>
</table>
</div>
<style>
.we-table-wrap{
max-width:76rem;margin:0 auto;
background:var(--bg2);border:1px solid var(--border);
border-radius:var(--radius);overflow:hidden;
}
.we-table{width:100%;border-collapse:collapse;font-family:var(--font-body)}
.we-table-cap{
caption-side:top;text-align:left;padding:26px 28px 18px;
font-family:var(--font-display);font-size:11.5px;font-weight:600;
letter-spacing:.16em;text-transform:uppercase;color:var(--accent-text);
}
.we-table thead th{
padding:12px 28px;text-align:left;
background:var(--bg3);
border-top:1px solid var(--border);border-bottom:1px solid var(--border);
font-family:var(--font-display);font-size:11px;font-weight:600;
letter-spacing:.1em;text-transform:uppercase;color:var(--text3);
white-space:nowrap;
}
.we-table td{
padding:18px 28px;font-size:14.5px;color:var(--text2);
border-bottom:1px solid var(--border);
}
.we-table tbody tr:last-child td{border-bottom:0}
.we-td-strong{
font-family:var(--font-display);font-size:16px;font-weight:600;
letter-spacing:-.015em;color:var(--text);
}
/* Status is stated in words. The colour is confirmation, never the message. */
.we-pill{
display:inline-flex;align-items:center;padding:5px 12px;
border-radius:var(--radius-pill);border:1px solid transparent;
font-size:12px;font-weight:500;white-space:nowrap;
}
.we-pill-open{background:var(--sage-s);border-color:var(--sage);color:var(--sage)}
.we-pill-wait{background:var(--accent-s);border-color:var(--accent-b);color:var(--accent-text)}
.we-pill-full{background:var(--bg3);border-color:var(--border);color:var(--text3)}
/* Below 720px the table becomes cards. A table that scrolls sideways on a phone
hides its own right-hand columns, which is where the answer usually is. */
@media (max-width:720px){
.we-table thead{
position:absolute;width:1px;height:1px;overflow:hidden;
clip:rect(0 0 0 0);white-space:nowrap;
}
.we-table, .we-table tbody, .we-table tr, .we-table td{display:block;width:100%}
.we-table tbody tr{
padding:6px 0;border-bottom:1px solid var(--border);
}
.we-table tbody tr:last-child{border-bottom:0}
.we-table td{
display:flex;align-items:center;justify-content:space-between;gap:16px;
padding:9px 22px;border-bottom:0;
}
.we-table td::before{
content:attr(data-label);
font-family:var(--font-display);font-size:11px;font-weight:600;
letter-spacing:.1em;text-transform:uppercase;color:var(--text3);
flex:0 0 auto;
}
.we-table-cap{padding:22px 22px 14px}
}
</style>
Needs tokens.css on the page.