| Invoice | Status | Method | Amount |
|---|---|---|---|
| INV001 | Paid | Card | €250.00 |
| INV002 | Pending | Transfer | €150.00 |
| INV003 | Unpaid | Card | €350.00 |
Table
Data table with hairline rows, compact headers and muted hover. Wrap in .table to scroll on small screens.
INSTALL
1pnpm dlx @sayagodev/mo add tableOr import the files from the package (no copy):
1import "@sayagodev/mo/css/table.css";2import "@sayagodev/mo/js/data-table.js";
EXAMPLES
Recent invoices
| Invoice | Status | Method | Amount |
|---|---|---|---|
| INV001 | Paid | Credit Card | $250.00 |
| INV002 | Pending | PayPal | $150.00 |
| INV003 | Unpaid | Bank Transfer | $350.00 |
| INV004 | Paid | Credit Card | $450.00 |
| INV005 | Paid | PayPal | $550.00 |
| INV006 | Pending | Bank Transfer | $200.00 |
| INV007 | Unpaid | Credit Card | $300.00 |
| Total | $2,250.00 | ||
1<div class="table">2 <table>3 <caption>A list of your recent invoices.</caption>4 <thead>5 <tr><th>Invoice</th><th>Status</th><th>Method</th><th class="align-right">Amount</th></tr>6 </thead>7 <tbody>8 <tr><td><strong>INV001</strong></td><td>Paid</td><td>Credit Card</td><td class="align-right">$250.00</td></tr>9 <tr><td><strong>INV002</strong></td><td>Pending</td><td>PayPal</td><td class="align-right">$150.00</td></tr>10 <tr><td><strong>INV003</strong></td><td>Unpaid</td><td>Bank Transfer</td><td class="align-right">$350.00</td></tr>11 <tr><td><strong>INV004</strong></td><td>Paid</td><td>Credit Card</td><td class="align-right">$450.00</td></tr>12 <tr><td><strong>INV005</strong></td><td>Paid</td><td>PayPal</td><td class="align-right">$550.00</td></tr>13 <tr><td><strong>INV006</strong></td><td>Pending</td><td>Bank Transfer</td><td class="align-right">$200.00</td></tr>14 <tr><td><strong>INV007</strong></td><td>Unpaid</td><td>Credit Card</td><td class="align-right">$300.00</td></tr>15 </tbody>16 <tfoot>17 <tr><td colspan="3">Total</td><td class="align-right">$2,250.00</td></tr>18 </tfoot>19 </table>20</div>21
Row actions
| Product | Price | Actions |
|---|---|---|
| Wireless Mouse | $29.99 |
|
| Mechanical Keyboard | $129.99 |
|
| USB-C Hub | $49.99 |
|
1<div class="table">2 <table>3 <thead>4 <tr><th>Product</th><th>Price</th><th class="align-right">Actions</th></tr>5 </thead>6 <tbody>7 <tr>8 <td><strong>Wireless Mouse</strong></td>9 <td>$29.99</td>10 <td class="align-right">11 <mo-dropdown>12 <button type="button" class="ghost icon small" popovertarget="table-actions-menu-1" aria-label="Open menu">13 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">14 <circle cx="12" cy="12" r="1"></circle>15 <circle cx="19" cy="12" r="1"></circle>16 <circle cx="5" cy="12" r="1"></circle>17 </svg>18 </button>19 <menu popover id="table-actions-menu-1">20 <button role="menuitem">Edit</button>21 <button role="menuitem">Duplicate</button>22 <hr />23 <button role="menuitem" data-variant="danger">Delete</button>24 </menu>25 </mo-dropdown>26 </td>27 </tr>28 <tr>29 <td><strong>Mechanical Keyboard</strong></td>30 <td>$129.99</td>31 <td class="align-right">32 <mo-dropdown>33 <button type="button" class="ghost icon small" popovertarget="table-actions-menu-2" aria-label="Open menu">34 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">35 <circle cx="12" cy="12" r="1"></circle>36 <circle cx="19" cy="12" r="1"></circle>37 <circle cx="5" cy="12" r="1"></circle>38 </svg>39 </button>40 <menu popover id="table-actions-menu-2">41 <button role="menuitem">Edit</button>42 <button role="menuitem">Duplicate</button>43 <hr />44 <button role="menuitem" data-variant="danger">Delete</button>45 </menu>46 </mo-dropdown>47 </td>48 </tr>49 <tr>50 <td><strong>USB-C Hub</strong></td>51 <td>$49.99</td>52 <td class="align-right">53 <mo-dropdown>54 <button type="button" class="ghost icon small" popovertarget="table-actions-menu-3" aria-label="Open menu">55 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">56 <circle cx="12" cy="12" r="1"></circle>57 <circle cx="19" cy="12" r="1"></circle>58 <circle cx="5" cy="12" r="1"></circle>59 </svg>60 </button>61 <menu popover id="table-actions-menu-3">62 <button role="menuitem">Edit</button>63 <button role="menuitem">Duplicate</button>64 <hr />65 <button role="menuitem" data-variant="danger">Delete</button>66 </menu>67 </mo-dropdown>68 </td>69 </tr>70 </tbody>71 </table>72</div>73
Footer totals
| Invoice | Status | Method | Amount |
|---|---|---|---|
| INV001 | Paid | Credit Card | $250.00 |
| INV002 | Pending | PayPal | $150.00 |
| INV003 | Unpaid | Bank Transfer | $350.00 |
| Total | $750.00 | ||
1<div class="table">2 <table>3 <caption>A list of your recent invoices.</caption>4 <thead>5 <tr><th>Invoice</th><th>Status</th><th>Method</th><th class="align-right">Amount</th></tr>6 </thead>7 <tbody>8 <tr><td><strong>INV001</strong></td><td>Paid</td><td>Credit Card</td><td class="align-right">$250.00</td></tr>9 <tr><td><strong>INV002</strong></td><td>Pending</td><td>PayPal</td><td class="align-right">$150.00</td></tr>10 <tr><td><strong>INV003</strong></td><td>Unpaid</td><td>Bank Transfer</td><td class="align-right">$350.00</td></tr>11 </tbody>12 <tfoot>13 <tr><td colspan="3">Total</td><td class="align-right">$750.00</td></tr>14 </tfoot>15 </table>16</div>17
Payment details
1<article class="card flush">2 <div class="table">3 <table>4 <thead>5 <tr><th>Invoice</th><th>Status</th><th>Method</th><th class="align-right">Amount</th></tr>6 </thead>7 <tbody>8 <tr>9 <td>INV001</td>10 <td><span class="badge" data-variant="success">Paid</span></td>11 <td>Card</td>12 <td class="align-end">€250.00</td>13 </tr>14 <tr>15 <td>INV002</td>16 <td><span class="badge" data-variant="secondary">Pending</span></td>17 <td>Transfer</td>18 <td class="align-end">€150.00</td>19 </tr>20 <tr>21 <td>INV003</td>22 <td><span class="badge outline" data-variant="danger">Unpaid</span></td>23 <td>Card</td>24 <td class="align-end">€350.00</td>25 </tr>26 </tbody>27 </table>28 </div>29</article>30
Sortable columns
| INV001 | Paid | Credit Card | $250.00 |
| INV002 | Pending | PayPal | $150.00 |
| INV003 | Unpaid | Bank Transfer | $350.00 |
| INV004 | Paid | Credit Card | $450.00 |
| INV005 | Paid | PayPal | $550.00 |
| INV006 | Pending | Bank Transfer | $200.00 |
| INV007 | Unpaid | Credit Card | $50.00 |
1<article class="card flush">2 <mo-data-table>3 <div class="table">4 <table>5 <caption>A list of your recent invoices.</caption>6 <thead>7 <tr>8 <th data-sortable><button type="button">Invoice</button></th>9 <th data-sortable><button type="button">Status</button></th>10 <th data-sortable><button type="button">Method</button></th>11 <th data-sortable aria-sort="ascending"><button type="button">Amount</button></th>12 </tr>13 </thead>14 <tbody>15 <tr><td>INV001</td><td><span class="badge" data-variant="success">Paid</span></td><td>Credit Card</td><td>$250.00</td></tr>16 <tr><td>INV002</td><td><span class="badge" data-variant="secondary">Pending</span></td><td>PayPal</td><td>$150.00</td></tr>17 <tr><td>INV003</td><td><span class="badge outline" data-variant="danger">Unpaid</span></td><td>Bank Transfer</td><td>$350.00</td></tr>18 <tr><td>INV004</td><td><span class="badge" data-variant="success">Paid</span></td><td>Credit Card</td><td>$450.00</td></tr>19 <tr><td>INV005</td><td><span class="badge" data-variant="success">Paid</span></td><td>PayPal</td><td>$550.00</td></tr>20 <tr><td>INV006</td><td><span class="badge" data-variant="secondary">Pending</span></td><td>Bank Transfer</td><td>$200.00</td></tr>21 <tr><td>INV007</td><td><span class="badge outline" data-variant="danger">Unpaid</span></td><td>Credit Card</td><td>$50.00</td></tr>22 </tbody>23 </table>24 </div>25 </mo-data-table>26</article>27
Row selection
| Status | Priority | ||
|---|---|---|---|
| Refactor auth flow | Done | High | |
| Ship dark mode | In Progress | Medium | |
| Update docs | Backlog | Low | |
| Fix pagination bug | Done | High |
1<article class="card flush">2 <mo-data-table>3 <div class="table">4 <table>5 <thead>6 <tr>7 <th data-select-all><input type="checkbox" aria-label="Select all" /></th>8 <th data-sortable><button type="button">Task</button></th>9 <th>Status</th>10 <th>Priority</th>11 </tr>12 </thead>13 <tbody>14 <tr data-selected>15 <td data-select><input type="checkbox" aria-label="Select row" checked /></td>16 <td><strong>Refactor auth flow</strong></td>17 <td><span class="badge" data-variant="success">Done</span></td>18 <td>High</td>19 </tr>20 <tr>21 <td data-select><input type="checkbox" aria-label="Select row" /></td>22 <td><strong>Ship dark mode</strong></td>23 <td><span class="badge">In Progress</span></td>24 <td>Medium</td>25 </tr>26 <tr>27 <td data-select><input type="checkbox" aria-label="Select row" /></td>28 <td><strong>Update docs</strong></td>29 <td><span class="badge outline" data-variant="secondary">Backlog</span></td>30 <td>Low</td>31 </tr>32 <tr data-selected>33 <td data-select><input type="checkbox" aria-label="Select row" checked /></td>34 <td><strong>Fix pagination bug</strong></td>35 <td><span class="badge" data-variant="success">Done</span></td>36 <td>High</td>37 </tr>38 </tbody>39 </table>40 </div>41 <footer data-table-pagination>42 <span data-table-info></span>43 <nav class="pagination" aria-label="Table pagination">44 <ul>45 <li><button type="button" data-table-prev aria-label="Previous page">‹ Previous</button></li>46 <li><button type="button" data-table-next aria-label="Next page">Next ›</button></li>47 </ul>48 </nav>49 </footer>50 </mo-data-table>51</article>52
Pagination
| Invoice | Status | Method | Amount |
|---|---|---|---|
| INV001 | Paid | Credit Card | $250.00 |
| INV002 | Pending | PayPal | $150.00 |
| INV003 | Unpaid | Bank Transfer | $350.00 |
| INV004 | Paid | Credit Card | $450.00 |
| INV005 | Paid | PayPal | $550.00 |
| INV006 | Pending | Bank Transfer | $200.00 |
| INV007 | Unpaid | Credit Card | $50.00 |
| INV008 | Paid | PayPal | $100.00 |
| INV009 | Paid | Credit Card | $800.00 |
| INV010 | Unpaid | Bank Transfer | $300.00 |
| INV011 | Paid | PayPal | $120.00 |
| INV012 | Pending | Credit Card | $640.00 |
| INV013 | Paid | Bank Transfer | $75.00 |
| INV014 | Paid | Credit Card | $500.00 |
| INV015 | Unpaid | PayPal | $90.00 |
| INV016 | Pending | Credit Card | $275.00 |
| INV017 | Paid | Bank Transfer | $330.00 |
| INV018 | Paid | Credit Card | $210.00 |
| INV019 | Unpaid | PayPal | $60.00 |
| INV020 | Paid | Credit Card | $480.00 |
| INV021 | Pending | Bank Transfer | $185.00 |
| INV022 | Paid | PayPal | $95.00 |
1<article class="card flush">2 <mo-data-table data-page-size="8">3 <div class="table">4 <table>5 <caption>Your recent payments across all workspaces.</caption>6 <thead>7 <tr>8 <th>Invoice</th>9 <th>Status</th>10 <th>Method</th>11 <th class="align-right">Amount</th>12 </tr>13 </thead>14 <tbody>15 <tr><td>INV001</td><td><span class="badge" data-variant="success">Paid</span></td><td>Credit Card</td><td>$250.00</td></tr>16 <tr><td>INV002</td><td><span class="badge" data-variant="secondary">Pending</span></td><td>PayPal</td><td>$150.00</td></tr>17 <tr><td>INV003</td><td><span class="badge outline" data-variant="danger">Unpaid</span></td><td>Bank Transfer</td><td>$350.00</td></tr>18 <tr><td>INV004</td><td><span class="badge" data-variant="success">Paid</span></td><td>Credit Card</td><td>$450.00</td></tr>19 <tr><td>INV005</td><td><span class="badge" data-variant="success">Paid</span></td><td>PayPal</td><td>$550.00</td></tr>20 <tr><td>INV006</td><td><span class="badge" data-variant="secondary">Pending</span></td><td>Bank Transfer</td><td>$200.00</td></tr>21 <tr><td>INV007</td><td><span class="badge outline" data-variant="danger">Unpaid</span></td><td>Credit Card</td><td>$50.00</td></tr>22 <tr><td>INV008</td><td><span class="badge" data-variant="success">Paid</span></td><td>PayPal</td><td>$100.00</td></tr>23 <tr><td>INV009</td><td><span class="badge" data-variant="success">Paid</span></td><td>Credit Card</td><td>$800.00</td></tr>24 <tr><td>INV010</td><td><span class="badge outline" data-variant="danger">Unpaid</span></td><td>Bank Transfer</td><td>$300.00</td></tr>25 <tr><td>INV011</td><td><span class="badge" data-variant="success">Paid</span></td><td>PayPal</td><td>$120.00</td></tr>26 <tr><td>INV012</td><td><span class="badge" data-variant="secondary">Pending</span></td><td>Credit Card</td><td>$640.00</td></tr>27 <tr><td>INV013</td><td><span class="badge" data-variant="success">Paid</span></td><td>Bank Transfer</td><td>$75.00</td></tr>28 <tr><td>INV014</td><td><span class="badge" data-variant="success">Paid</span></td><td>Credit Card</td><td>$500.00</td></tr>29 <tr><td>INV015</td><td><span class="badge outline" data-variant="danger">Unpaid</span></td><td>PayPal</td><td>$90.00</td></tr>30 <tr><td>INV016</td><td><span class="badge" data-variant="secondary">Pending</span></td><td>Credit Card</td><td>$275.00</td></tr>31 <tr><td>INV017</td><td><span class="badge" data-variant="success">Paid</span></td><td>Bank Transfer</td><td>$330.00</td></tr>32 <tr><td>INV018</td><td><span class="badge" data-variant="success">Paid</span></td><td>Credit Card</td><td>$210.00</td></tr>33 <tr><td>INV019</td><td><span class="badge outline" data-variant="danger">Unpaid</span></td><td>PayPal</td><td>$60.00</td></tr>34 <tr><td>INV020</td><td><span class="badge" data-variant="success">Paid</span></td><td>Credit Card</td><td>$480.00</td></tr>35 <tr><td>INV021</td><td><span class="badge" data-variant="secondary">Pending</span></td><td>Bank Transfer</td><td>$185.00</td></tr>36 <tr><td>INV022</td><td><span class="badge" data-variant="success">Paid</span></td><td>PayPal</td><td>$95.00</td></tr>37 </tbody>38 </table>39 </div>40 <footer data-table-pagination>41 <div class="flex items-center justify-between gap-4">42 <label class="hstack items-center gap-2">43 Rows per page44 <mo-select data-placeholder="8" style="width: 4.375rem">45 <button type="button" popovertarget="ex-tbl-rows" data-select-trigger aria-label="Rows per page"></button>46 <ul popover id="ex-tbl-rows" class="popover" role="listbox" aria-label="Rows per page">47 <li><button type="button" role="option" value="8" aria-selected="true">8</button></li>48 <li><button type="button" role="option" value="10">10</button></li>49 <li><button type="button" role="option" value="20">20</button></li>50 <li><button type="button" role="option" value="50">50</button></li>51 </ul>52 </mo-select>53 </label>54 <div class="hstack items-center gap-2">55 <span data-table-info>22 row(s)</span>56 <div class="hstack gap-1">57 <button type="button" data-table-prev class="outline small icon" aria-label="Previous page" disabled>58 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6" /></svg>59 </button>60 <button type="button" data-table-next class="outline small icon" aria-label="Next page">61 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m9 18 6-6-6-6" /></svg>62 </button>63 </div>64 </div>65 </div>66 </footer>67 </mo-data-table>68</article>69
Filtering
| Task | Status | Assignee |
|---|---|---|
| Refactor auth flow | Done | [email protected] |
| Ship dark mode | In Progress | [email protected] |
| Update docs | Backlog | [email protected] |
| Fix pagination bug | Done | [email protected] |
| Migrate payments API | In Progress | [email protected] |
| Audit accessibility | Backlog | [email protected] |
1<article class="card flush">2 <mo-data-table class="vstack gap-4">3 <input type="text" data-table-filter placeholder="Filter tasks…" aria-label="Filter tasks" />4 <div class="table">5 <table>6 <thead>7 <tr>8 <th>Task</th>9 <th>Status</th>10 <th>Assignee</th>11 </tr>12 </thead>13 <tbody>14 <tr><td><strong>Refactor auth flow</strong></td><td><span class="badge" data-variant="success">Done</span></td><td>[email protected]</td></tr>15 <tr><td><strong>Ship dark mode</strong></td><td><span class="badge">In Progress</span></td><td>[email protected]</td></tr>16 <tr><td><strong>Update docs</strong></td><td><span class="badge outline" data-variant="secondary">Backlog</span></td><td>[email protected]</td></tr>17 <tr><td><strong>Fix pagination bug</strong></td><td><span class="badge" data-variant="success">Done</span></td><td>[email protected]</td></tr>18 <tr><td><strong>Migrate payments API</strong></td><td><span class="badge">In Progress</span></td><td>[email protected]</td></tr>19 <tr><td><strong>Audit accessibility</strong></td><td><span class="badge outline" data-variant="secondary">Backlog</span></td><td>[email protected]</td></tr>20 </tbody>21 </table>22 </div>23 </mo-data-table>24</article>25
USAGE
1<div class="table"> <!-- horizontal scroll wrapper -->2 <table>3 <caption>…</caption>4 <thead><tr><th>…</th></tr></thead>5 <tbody><tr><td>…</td></tr></tbody>6 <tfoot><tr><td>…</td></tr></tfoot>7 </table>8</div>
API
NAMEKINDDESCRIPTION
.tableclassScroll container for small screens
tfootelementMuted surface, medium weight
captionelementStyled below the table
--mo-table-cell-*tokenCell padding hooks
FILES
src/css/table.csssrc/js/data-table.jsInstall:
mo add tableTHEMING · PLAYGROUND
This is the exact component from the examples above, wearing a completely different theme. Edit the tokens and --mo-* hooks in the editor — autocomplete knows them all — and the preview obeys in real time. Your CSS always wins.
Global tokens
--border--foreground--muted--muted-foreground--ringComponent hooks
--mo-table-cell-padding-x--mo-table-cell-padding-yPreview
Your CSS
Try it: type --mo- for the component hooks, or edit any value — the preview obeys in real time.