Rendering & overrides
The template is deliberately dumb. Everything that needs a decision has already been made, so an override changes presentation and cannot accidentally change correctness.
What comes out
<div class="legs" data-legs="{…}" data-legs-responsive="scroll">
<div class="legs-controls" data-legs-controls hidden></div>
<div class="legs-scroll">
<table id="legs-price-list" class="legs-table legs-striped legs-hover legs-bordered">
<caption class="legs-caption legs-caption-top">Pricing, updated monthly</caption>
<thead>
<tr>
<th scope="col" class="legs-align-left" data-legs-sort-col="0" data-legs-sort-as="text" aria-sort="none">Plan</th>
<th scope="col" class="legs-align-left" data-legs-sort-col="1" data-legs-sort-as="number" aria-sort="none">Price</th>
</tr>
</thead>
<tbody>
<tr>
<td class="legs-align-left" data-legs-label="Plan">Basic</td>
<td class="legs-align-left" data-legs-sort="10" data-legs-label="Price">$10.00</td>
</tr>
</tbody>
</table>
</div>
<div class="legs-status" role="status" aria-live="polite"></div>
</div>
Three things in there are doing real work:
data-legs-sort— the comparable value, computed on the server. The browser never has to guess what a currency symbol or a date format means.data-legs-label— what this cell is called when the table stacks on a narrow screen. It comes from the header row.aria-sort— kept current as the visitor sorts, so a screen reader announces the change.
Taking the markup over
Copy the plugin's src/templates/_render/table.twig to templates/_legs/table.twig in your own site. Legs renders yours instead, with these variables:
| Variable | What it is |
|---|---|
head, body, foot | Rows, already split. Each row has cells; each cell has html, colspan, rowspan, align, label and sortValue. |
columns | Per column: align, width, sortable, sortAs (already sniffed), label. |
options | The resolved render options, after edition downgrades. |
data | The grid itself, if you want it raw. |
table, caption, handle, id | The element (null for inline field values), its caption, and the id to put on the table. |
texts | Translated strings the runtime injects. |
Cells arriving as html have already been purified or escaped according to the Allow HTML in cells setting, so {{ cell.html|raw }} is correct and safe in an override.
Merged cells are handled for you: a cell swallowed by a merge is simply not in the row, and the anchor cell carries the spans. Hidden rows and columns are already gone.
Keeping the runtime
If you want sorting and search in your own markup, keep three things: the data-legs config on the wrapper, data-legs-sort-col on the header cells that should be sortable, and data-legs-controls where the search box and the count should go. Everything else is yours.
Turning Legs' own CSS off
Set registerCss to false in the settings — or in config/legs.php — and no stylesheet of ours loads. The markup and the runtime work without it.