md-table spec card
Identity · when to use / when NOT · decision cues · behavioural contract · do/don't · anti-patterns · full API. Paste into your agent when you're implementing with this component.
The composable table primitive. You author the rows; the table provides the CSS-Grid layout, sticky and frozen headers, column pinning, sort and selection state, striping, and loading / empty states.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Invoices" striped>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="inv-1">
<md-table-cell head scope="row">Acme Corp</md-table-cell>
<md-table-cell numeric>$1,200.00</md-table-cell>
<md-table-cell>Paid</md-table-cell>
</md-table-row>
<md-table-row value="inv-2">
<md-table-cell head scope="row">Globex</md-table-cell>
<md-table-cell numeric>$3,120.00</md-table-cell>
<md-table-cell>Pending</md-table-cell>
</md-table-row>
<md-table-row value="inv-3">
<md-table-cell head scope="row">Initech</md-table-cell>
<md-table-cell numeric>$480.00</md-table-cell>
<md-table-cell>Overdue</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>import { MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead, MdTableRow } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="2fr 1fr 1fr" label="Invoices" striped>
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="inv-1">
<MdTableCell head scope="row">Acme Corp</MdTableCell>
<MdTableCell numeric>$1,200.00</MdTableCell>
<MdTableCell>Paid</MdTableCell>
</MdTableRow>
<MdTableRow value="inv-2">
<MdTableCell head scope="row">Globex</MdTableCell>
<MdTableCell numeric>$3,120.00</MdTableCell>
<MdTableCell>Pending</MdTableCell>
</MdTableRow>
<MdTableRow value="inv-3">
<MdTableCell head scope="row">Initech</MdTableCell>
<MdTableCell numeric>$480.00</MdTableCell>
<MdTableCell>Overdue</MdTableCell>
</MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Invoices" striped>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="inv-1">
<md-table-cell head scope="row">Acme Corp</md-table-cell>
<md-table-cell numeric>$1,200.00</md-table-cell>
<md-table-cell>Paid</md-table-cell>
</md-table-row>
<md-table-row value="inv-2">
<md-table-cell head scope="row">Globex</md-table-cell>
<md-table-cell numeric>$3,120.00</md-table-cell>
<md-table-cell>Pending</md-table-cell>
</md-table-row>
<md-table-row value="inv-3">
<md-table-cell head scope="row">Initech</md-table-cell>
<md-table-cell numeric>$480.00</md-table-cell>
<md-table-cell>Overdue</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Invoices" striped>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="inv-1">
<md-table-cell head scope="row">Acme Corp</md-table-cell>
<md-table-cell numeric>$1,200.00</md-table-cell>
<md-table-cell>Paid</md-table-cell>
</md-table-row>
<md-table-row value="inv-2">
<md-table-cell head scope="row">Globex</md-table-cell>
<md-table-cell numeric>$3,120.00</md-table-cell>
<md-table-cell>Pending</md-table-cell>
</md-table-row>
<md-table-row value="inv-3">
<md-table-cell head scope="row">Initech</md-table-cell>
<md-table-cell numeric>$480.00</md-table-cell>
<md-table-cell>Overdue</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</template><script>
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Invoices" striped>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="inv-1">
<md-table-cell head scope="row">Acme Corp</md-table-cell>
<md-table-cell numeric>$1,200.00</md-table-cell>
<md-table-cell>Paid</md-table-cell>
</md-table-row>
<md-table-row value="inv-2">
<md-table-cell head scope="row">Globex</md-table-cell>
<md-table-cell numeric>$3,120.00</md-table-cell>
<md-table-cell>Pending</md-table-cell>
</md-table-row>
<md-table-row value="inv-3">
<md-table-cell head scope="row">Initech</md-table-cell>
<md-table-cell numeric>$480.00</md-table-cell>
<md-table-cell>Overdue</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>
Already installed? See the
Installation guide for one-time package setup
(core + tokens, fonts). Each tab below shows two patterns for using
md-table in your project: Option A registers every AWC UI
component at once (simplest), Option B imports only this component for
tree-shake-friendly bundles.
<!-- ─── Option A: global registration (all components) ─── -->
<script type="module">
import '@awc-ui/core/define';
</script>
<!-- ─── Option B: single import (tree-shake only md-table) ─── -->
<script type="module">
import '@awc-ui/core/components/md-table';
</script>
<md-table></md-table>// ─── Option A: typed React wrapper (registers all components) ───
// Importing from '@awc-ui/react' calls defineCustomElements() as a
// side effect, so every md-* element becomes available in the browser.
import { MdTable } from '@awc-ui/react';
export function Example() {
return <MdTable></MdTable>;
}
// ─── Option B: single import (tree-shake to only md-table) ───
// Skip the wrapper and use the raw custom element. Smallest bundle,
// but you lose typed props/events on JSX.
import '@awc-ui/core/components/md-table';
export function ExampleTreeShaken() {
return <md-table></md-table>;
}// ─── Option A: schema module (any md-* element accepted) ───
// Pair with `defineCustomElements(window)` in main.ts.
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@Component({
standalone: true,
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `<md-table></md-table>`,
})
export class ExampleComponent {}
// ─── Option B: typed directive (tree-shake friendly) ───
// Pair with `import '@awc-ui/core/components/md-table'` in main.ts.
import { Component } from '@angular/core';
import { MdTable } from '@awc-ui/angular';
@Component({
standalone: true,
imports: [MdTable],
template: `<md-table></md-table>`,
})
export class ExampleTreeShakenComponent {}<!-- ─── Option A: typed Vue wrapper (registers all components) ─── -->
<script setup lang="ts">
import { MdTable } from '@awc-ui/vue';
</script>
<template>
<MdTable></MdTable>
</template>
<!-- ─── Option B: single import (tree-shake to only md-table) ─── -->
<script setup lang="ts">
import '@awc-ui/core/components/md-table';
</script>
<template>
<md-table></md-table>
</template><!-- ─── Option A: global registration (done once in main entry) ─── -->
<!-- main.ts: -->
<!-- import { defineCustomElements } from '@awc-ui/svelte'; -->
<!-- defineCustomElements(window); -->
<md-table></md-table>
<!-- ─── Option B: single import (tree-shake to only md-table) ─── -->
<script lang="ts">
import '@awc-ui/core/components/md-table';
</script>
<md-table></md-table>| Situation | Use instead |
|---|---|
| A vertical list of records | md-list |
| Rich, self-contained items | a md-card collection |
| Hierarchy / reporting lines | md-organization-chart |
| Comparing two values | Plain text |
Layout (the old <table> misuse) | CSS Grid |
<table>This is the single most important thing to internalise. The shadow
.md-table__grid owns the column tracks; each md-table-row is a real grid
item that lays its cells on those shared tracks via grid-template-columns: subgrid. The rowgroups — md-table-head,
md-table-body,
md-table-foot — are display: contents, so their
rows land directly on the parent’s tracks.
Two consequences follow, and both bite:
| Need | Setting |
|---|---|
| Explicit column widths | column-template — a raw CSS grid-template-columns value (minmax(), fr, auto all allowed) |
| Just a column count | columns — becomes repeat(<columns>, minmax(0, 1fr)) |
| A floor before horizontal scrolling kicks in | min-width (or --md-table-min-width) |
column-template wins over columns when both are set.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined"><md-table columns="3" label="Derived from the columns prop">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Equal</md-table-cell>
<md-table-cell head scope="col">Thirds</md-table-cell>
<md-table-cell head scope="col">Each</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell>A</md-table-cell><md-table-cell>B</md-table-cell><md-table-cell>C</md-table-cell></md-table-row>
</md-table-body>
</md-table></md-table-container>
<md-table-container variant="outlined" style="margin-block-start: 20px;"><md-table column-template="minmax(160px, 2fr) 1fr 120px" label="Explicit template">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Flexible</md-table-cell>
<md-table-cell head scope="col">1fr</md-table-cell>
<md-table-cell head scope="col" numeric>120px</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell>A</md-table-cell><md-table-cell>B</md-table-cell><md-table-cell numeric>42</md-table-cell></md-table-row>
</md-table-body>
</md-table></md-table-container>import { MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead, MdTableRow } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdTableContainer variant="outlined"><MdTable columns="3" label="Derived from the columns prop">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Equal</MdTableCell>
<MdTableCell head scope="col">Thirds</MdTableCell>
<MdTableCell head scope="col">Each</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow><MdTableCell>A</MdTableCell><MdTableCell>B</MdTableCell><MdTableCell>C</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable></MdTableContainer>
<MdTableContainer variant="outlined" style={{ marginBlockStart: '20px' }}><MdTable columnTemplate="minmax(160px, 2fr) 1fr 120px" label="Explicit template">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Flexible</MdTableCell>
<MdTableCell head scope="col">1fr</MdTableCell>
<MdTableCell head scope="col" numeric>120px</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow><MdTableCell>A</MdTableCell><MdTableCell>B</MdTableCell><MdTableCell numeric>42</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable></MdTableContainer>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-table-container variant="outlined"><md-table columns="3" label="Derived from the columns prop">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Equal</md-table-cell>
<md-table-cell head scope="col">Thirds</md-table-cell>
<md-table-cell head scope="col">Each</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell>A</md-table-cell><md-table-cell>B</md-table-cell><md-table-cell>C</md-table-cell></md-table-row>
</md-table-body>
</md-table></md-table-container>
<md-table-container variant="outlined" style="margin-block-start: 20px;"><md-table column-template="minmax(160px, 2fr) 1fr 120px" label="Explicit template">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Flexible</md-table-cell>
<md-table-cell head scope="col">1fr</md-table-cell>
<md-table-cell head scope="col" numeric>120px</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell>A</md-table-cell><md-table-cell>B</md-table-cell><md-table-cell numeric>42</md-table-cell></md-table-row>
</md-table-body>
</md-table></md-table-container><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-table-container variant="outlined"><md-table columns="3" label="Derived from the columns prop">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Equal</md-table-cell>
<md-table-cell head scope="col">Thirds</md-table-cell>
<md-table-cell head scope="col">Each</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell>A</md-table-cell><md-table-cell>B</md-table-cell><md-table-cell>C</md-table-cell></md-table-row>
</md-table-body>
</md-table></md-table-container>
<md-table-container variant="outlined" style="margin-block-start: 20px;"><md-table column-template="minmax(160px, 2fr) 1fr 120px" label="Explicit template">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Flexible</md-table-cell>
<md-table-cell head scope="col">1fr</md-table-cell>
<md-table-cell head scope="col" numeric>120px</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell>A</md-table-cell><md-table-cell>B</md-table-cell><md-table-cell numeric>42</md-table-cell></md-table-row>
</md-table-body>
</md-table></md-table-container>
</template><script>
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined"><md-table columns="3" label="Derived from the columns prop">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Equal</md-table-cell>
<md-table-cell head scope="col">Thirds</md-table-cell>
<md-table-cell head scope="col">Each</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell>A</md-table-cell><md-table-cell>B</md-table-cell><md-table-cell>C</md-table-cell></md-table-row>
</md-table-body>
</md-table></md-table-container>
<md-table-container variant="outlined" style="margin-block-start: 20px;"><md-table column-template="minmax(160px, 2fr) 1fr 120px" label="Explicit template">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Flexible</md-table-cell>
<md-table-cell head scope="col">1fr</md-table-cell>
<md-table-cell head scope="col" numeric>120px</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell>A</md-table-cell><md-table-cell>B</md-table-cell><md-table-cell numeric>42</md-table-cell></md-table-row>
</md-table-body>
</md-table></md-table-container>Rows being real boxes is what makes hover, stripes, dividers, sticky headers and focus rings work — and it means a wrong cell count skews one row rather than the whole table.
Two Storybook stories put the whole surface together at once: a full data table (toolbar with quick-filter and CSV export, selection, sorting, sticky header and column, pagination) and a fully customised one.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined" max-height="248px">
<md-table-toolbar slot="top" headline="Invoices" supporting-text="Q1 2025"></md-table-toolbar>
<md-table column-template="2fr 1fr 1fr" sticky-header striped label="Invoices">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">
<md-table-sort-label column="name">Name</md-table-sort-label>
</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="inv-1">
<md-table-cell head scope="row">Acme Corp</md-table-cell>
<md-table-cell numeric>$1,200</md-table-cell>
<md-table-cell><md-chip label="Paid" color="success"></md-chip></md-table-cell>
</md-table-row>
<md-table-row value="inv-2">
<md-table-cell head scope="row">Globex</md-table-cell>
<md-table-cell numeric>$3,120</md-table-cell>
<md-table-cell><md-chip label="Pending" color="warning"></md-chip></md-table-cell>
</md-table-row>
<md-table-row value="inv-3">
<md-table-cell head scope="row">Initech</md-table-cell>
<md-table-cell numeric>$480</md-table-cell>
<md-table-cell><md-chip label="Overdue" color="error"></md-chip></md-table-cell>
</md-table-row>
<md-table-row value="inv-4">
<md-table-cell head scope="row">Umbrella</md-table-cell>
<md-table-cell numeric>$2,450</md-table-cell>
<md-table-cell><md-chip label="Paid" color="success"></md-chip></md-table-cell>
</md-table-row>
</md-table-body>
<md-table-foot>
<md-table-row rowgroup="foot" selectable="false">
<md-table-cell head scope="row">Total</md-table-cell>
<md-table-cell numeric>$7,250</md-table-cell>
<md-table-cell></md-table-cell>
</md-table-row>
</md-table-foot>
</md-table>
<md-table-pagination slot="bottom" count="4" rows-per-page="5"></md-table-pagination>
</md-table-container>import { MdChip, MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableFoot, MdTableHead, MdTablePagination, MdTableRow, MdTableSortLabel, MdTableToolbar } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdTableContainer variant="outlined" maxHeight="248px">
<MdTableToolbar slot="top" headline="Invoices" supportingText="Q1 2025"></MdTableToolbar>
<MdTable columnTemplate="2fr 1fr 1fr" stickyHeader striped label="Invoices">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">
<MdTableSortLabel column="name">Name</MdTableSortLabel>
</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="inv-1">
<MdTableCell head scope="row">Acme Corp</MdTableCell>
<MdTableCell numeric>$1,200</MdTableCell>
<MdTableCell><MdChip label="Paid" color="success"></MdChip></MdTableCell>
</MdTableRow>
<MdTableRow value="inv-2">
<MdTableCell head scope="row">Globex</MdTableCell>
<MdTableCell numeric>$3,120</MdTableCell>
<MdTableCell><MdChip label="Pending" color="warning"></MdChip></MdTableCell>
</MdTableRow>
<MdTableRow value="inv-3">
<MdTableCell head scope="row">Initech</MdTableCell>
<MdTableCell numeric>$480</MdTableCell>
<MdTableCell><MdChip label="Overdue" color="error"></MdChip></MdTableCell>
</MdTableRow>
<MdTableRow value="inv-4">
<MdTableCell head scope="row">Umbrella</MdTableCell>
<MdTableCell numeric>$2,450</MdTableCell>
<MdTableCell><MdChip label="Paid" color="success"></MdChip></MdTableCell>
</MdTableRow>
</MdTableBody>
<MdTableFoot>
<MdTableRow rowgroup="foot" selectable="false">
<MdTableCell head scope="row">Total</MdTableCell>
<MdTableCell numeric>$7,250</MdTableCell>
<MdTableCell></MdTableCell>
</MdTableRow>
</MdTableFoot>
</MdTable>
<MdTablePagination slot="bottom" count="4" rowsPerPage="5"></MdTablePagination>
</MdTableContainer>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-table-container variant="outlined" max-height="248px">
<md-table-toolbar slot="top" headline="Invoices" supporting-text="Q1 2025"></md-table-toolbar>
<md-table column-template="2fr 1fr 1fr" sticky-header striped label="Invoices">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">
<md-table-sort-label column="name">Name</md-table-sort-label>
</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="inv-1">
<md-table-cell head scope="row">Acme Corp</md-table-cell>
<md-table-cell numeric>$1,200</md-table-cell>
<md-table-cell><md-chip label="Paid" color="success"></md-chip></md-table-cell>
</md-table-row>
<md-table-row value="inv-2">
<md-table-cell head scope="row">Globex</md-table-cell>
<md-table-cell numeric>$3,120</md-table-cell>
<md-table-cell><md-chip label="Pending" color="warning"></md-chip></md-table-cell>
</md-table-row>
<md-table-row value="inv-3">
<md-table-cell head scope="row">Initech</md-table-cell>
<md-table-cell numeric>$480</md-table-cell>
<md-table-cell><md-chip label="Overdue" color="error"></md-chip></md-table-cell>
</md-table-row>
<md-table-row value="inv-4">
<md-table-cell head scope="row">Umbrella</md-table-cell>
<md-table-cell numeric>$2,450</md-table-cell>
<md-table-cell><md-chip label="Paid" color="success"></md-chip></md-table-cell>
</md-table-row>
</md-table-body>
<md-table-foot>
<md-table-row rowgroup="foot" selectable="false">
<md-table-cell head scope="row">Total</md-table-cell>
<md-table-cell numeric>$7,250</md-table-cell>
<md-table-cell></md-table-cell>
</md-table-row>
</md-table-foot>
</md-table>
<md-table-pagination slot="bottom" count="4" rows-per-page="5"></md-table-pagination>
</md-table-container><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-table-container variant="outlined" max-height="248px">
<md-table-toolbar slot="top" headline="Invoices" supporting-text="Q1 2025"></md-table-toolbar>
<md-table column-template="2fr 1fr 1fr" sticky-header striped label="Invoices">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">
<md-table-sort-label column="name">Name</md-table-sort-label>
</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="inv-1">
<md-table-cell head scope="row">Acme Corp</md-table-cell>
<md-table-cell numeric>$1,200</md-table-cell>
<md-table-cell><md-chip label="Paid" color="success"></md-chip></md-table-cell>
</md-table-row>
<md-table-row value="inv-2">
<md-table-cell head scope="row">Globex</md-table-cell>
<md-table-cell numeric>$3,120</md-table-cell>
<md-table-cell><md-chip label="Pending" color="warning"></md-chip></md-table-cell>
</md-table-row>
<md-table-row value="inv-3">
<md-table-cell head scope="row">Initech</md-table-cell>
<md-table-cell numeric>$480</md-table-cell>
<md-table-cell><md-chip label="Overdue" color="error"></md-chip></md-table-cell>
</md-table-row>
<md-table-row value="inv-4">
<md-table-cell head scope="row">Umbrella</md-table-cell>
<md-table-cell numeric>$2,450</md-table-cell>
<md-table-cell><md-chip label="Paid" color="success"></md-chip></md-table-cell>
</md-table-row>
</md-table-body>
<md-table-foot>
<md-table-row rowgroup="foot" selectable="false">
<md-table-cell head scope="row">Total</md-table-cell>
<md-table-cell numeric>$7,250</md-table-cell>
<md-table-cell></md-table-cell>
</md-table-row>
</md-table-foot>
</md-table>
<md-table-pagination slot="bottom" count="4" rows-per-page="5"></md-table-pagination>
</md-table-container>
</template><script>
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined" max-height="248px">
<md-table-toolbar slot="top" headline="Invoices" supporting-text="Q1 2025"></md-table-toolbar>
<md-table column-template="2fr 1fr 1fr" sticky-header striped label="Invoices">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">
<md-table-sort-label column="name">Name</md-table-sort-label>
</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="inv-1">
<md-table-cell head scope="row">Acme Corp</md-table-cell>
<md-table-cell numeric>$1,200</md-table-cell>
<md-table-cell><md-chip label="Paid" color="success"></md-chip></md-table-cell>
</md-table-row>
<md-table-row value="inv-2">
<md-table-cell head scope="row">Globex</md-table-cell>
<md-table-cell numeric>$3,120</md-table-cell>
<md-table-cell><md-chip label="Pending" color="warning"></md-chip></md-table-cell>
</md-table-row>
<md-table-row value="inv-3">
<md-table-cell head scope="row">Initech</md-table-cell>
<md-table-cell numeric>$480</md-table-cell>
<md-table-cell><md-chip label="Overdue" color="error"></md-chip></md-table-cell>
</md-table-row>
<md-table-row value="inv-4">
<md-table-cell head scope="row">Umbrella</md-table-cell>
<md-table-cell numeric>$2,450</md-table-cell>
<md-table-cell><md-chip label="Paid" color="success"></md-chip></md-table-cell>
</md-table-row>
</md-table-body>
<md-table-foot>
<md-table-row rowgroup="foot" selectable="false">
<md-table-cell head scope="row">Total</md-table-cell>
<md-table-cell numeric>$7,250</md-table-cell>
<md-table-cell></md-table-cell>
</md-table-row>
</md-table-foot>
</md-table>
<md-table-pagination slot="bottom" count="4" rows-per-page="5"></md-table-pagination>
</md-table-container>md-table-container is the surface and the
scroll box. Its top / bottom slots sit outside the scroll region —
that’s where the toolbar and pagination belong.rowgroup="head|body|foot" goes on each row, not just the wrapper. The
wrapper does not cascade it.head and scope together on
md-table-cell — see
Accessibility.md-table-container carries the surface treatment. elevated (the default)
floats the table on a shadow, outlined draws a hairline border instead,
filled sits on a tonal surface, and flat removes both — including the
background — so the table inherits whatever surface it sits on. Use flat
only inside something you have already framed; on a bare page its rows have
nothing to sit on.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div style="display: grid; gap: 20px;">
<md-table-container variant="elevated">
<md-table column-template="2fr 1fr 1fr" label="Elevated container">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">variant="elevated"</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Outlined container">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">variant="outlined"</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="filled">
<md-table column-template="2fr 1fr 1fr" label="Filled container">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">variant="filled"</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<!-- flat paints no surface of its own — it inherits the one it sits on.
Framed here the way you would actually use it, inside a panel. -->
<div style="padding: 16px; border-radius: 16px; background: var(--md-sys-color-surface);">
<md-table-container variant="flat">
<md-table column-template="2fr 1fr 1fr" label="Flat container">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">variant="flat"</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
</div>import { MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead, MdTableRow } from '@awc-ui/react';
export function Demo() {
return (
<>
<div style={{ display: 'grid', gap: '20px' }}>
<MdTableContainer variant="elevated">
<MdTable columnTemplate="2fr 1fr 1fr" label="Elevated container">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">variant="elevated"</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow><MdTableCell head scope="row">Acme Corp</MdTableCell><MdTableCell numeric>$1,200.00</MdTableCell><MdTableCell>Paid</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell head scope="row">Globex</MdTableCell><MdTableCell numeric>$3,120.00</MdTableCell><MdTableCell>Pending</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="2fr 1fr 1fr" label="Outlined container">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">variant="outlined"</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow><MdTableCell head scope="row">Acme Corp</MdTableCell><MdTableCell numeric>$1,200.00</MdTableCell><MdTableCell>Paid</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell head scope="row">Globex</MdTableCell><MdTableCell numeric>$3,120.00</MdTableCell><MdTableCell>Pending</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
<MdTableContainer variant="filled">
<MdTable columnTemplate="2fr 1fr 1fr" label="Filled container">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">variant="filled"</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow><MdTableCell head scope="row">Acme Corp</MdTableCell><MdTableCell numeric>$1,200.00</MdTableCell><MdTableCell>Paid</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell head scope="row">Globex</MdTableCell><MdTableCell numeric>$3,120.00</MdTableCell><MdTableCell>Pending</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
<!-- flat paints no surface of its own — it inherits the one it sits on.
Framed here the way you would actually use it, inside a panel. -->
<div style={{ padding: '16px', borderRadius: '16px', background: 'var(--md-sys-color-surface)' }}>
<MdTableContainer variant="flat">
<MdTable columnTemplate="2fr 1fr 1fr" label="Flat container">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">variant="flat"</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow><MdTableCell head scope="row">Acme Corp</MdTableCell><MdTableCell numeric>$1,200.00</MdTableCell><MdTableCell>Paid</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell head scope="row">Globex</MdTableCell><MdTableCell numeric>$3,120.00</MdTableCell><MdTableCell>Pending</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
</div>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div style="display: grid; gap: 20px;">
<md-table-container variant="elevated">
<md-table column-template="2fr 1fr 1fr" label="Elevated container">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">variant="elevated"</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Outlined container">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">variant="outlined"</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="filled">
<md-table column-template="2fr 1fr 1fr" label="Filled container">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">variant="filled"</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<!-- flat paints no surface of its own — it inherits the one it sits on.
Framed here the way you would actually use it, inside a panel. -->
<div style="padding: 16px; border-radius: 16px; background: var(--md-sys-color-surface);">
<md-table-container variant="flat">
<md-table column-template="2fr 1fr 1fr" label="Flat container">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">variant="flat"</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
</div><script setup>
import '@awc-ui/core/define';
</script>
<template>
<div style="display: grid; gap: 20px;">
<md-table-container variant="elevated">
<md-table column-template="2fr 1fr 1fr" label="Elevated container">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">variant="elevated"</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Outlined container">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">variant="outlined"</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="filled">
<md-table column-template="2fr 1fr 1fr" label="Filled container">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">variant="filled"</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<!-- flat paints no surface of its own — it inherits the one it sits on.
Framed here the way you would actually use it, inside a panel. -->
<div style="padding: 16px; border-radius: 16px; background: var(--md-sys-color-surface);">
<md-table-container variant="flat">
<md-table column-template="2fr 1fr 1fr" label="Flat container">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">variant="flat"</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
</div>
</template><script>
import '@awc-ui/core/define';
</script>
<div style="display: grid; gap: 20px;">
<md-table-container variant="elevated">
<md-table column-template="2fr 1fr 1fr" label="Elevated container">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">variant="elevated"</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Outlined container">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">variant="outlined"</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="filled">
<md-table column-template="2fr 1fr 1fr" label="Filled container">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">variant="filled"</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<!-- flat paints no surface of its own — it inherits the one it sits on.
Framed here the way you would actually use it, inside a panel. -->
<div style="padding: 16px; border-radius: 16px; background: var(--md-sys-color-surface);">
<md-table-container variant="flat">
<md-table column-template="2fr 1fr 1fr" label="Flat container">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">variant="flat"</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
</div>vibrant on the container tints the whole table with the primary/tertiary
container roles — a header band in primary, tinted stripes, and selection in
tertiary. Use it for a table that is the centrepiece of its screen; keep the
plain surface for tables that sit alongside other content.
<!-- index.html <head> — the icon font the components draw from -->
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&display=swap">
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-table-container variant="elevated" vibrant>
<md-table-toolbar slot="top" headline="Employees" supporting-text="Vibrant tonality" auto-bind>
<md-icon-button slot="actions" icon="filter_list" aria-label="Filter"></md-icon-button>
<md-icon-button slot="selection-actions" icon="delete" aria-label="Delete"></md-icon-button>
</md-table-toolbar>
<md-table id="vib-table" selection="multiple" striped column-template="auto 1.5fr 1.2fr 1fr 1fr" label="Vibrant employees">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox"><md-checkbox id="vib-all" aria-label="Select all rows"></md-checkbox></md-table-cell>
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col">Role</md-table-cell>
<md-table-cell head scope="col">Team</md-table-cell>
<md-table-cell head scope="col" numeric>Salary</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="v1"><md-table-cell padding="checkbox"><md-checkbox aria-label="Select Ada Lovelace"></md-checkbox></md-table-cell><md-table-cell head scope="row">Ada Lovelace</md-table-cell><md-table-cell>Mathematician</md-table-cell><md-table-cell>Engineering</md-table-cell><md-table-cell numeric>$120,000</md-table-cell></md-table-row>
<md-table-row value="v2"><md-table-cell padding="checkbox"><md-checkbox aria-label="Select Alan Turing"></md-checkbox></md-table-cell><md-table-cell head scope="row">Alan Turing</md-table-cell><md-table-cell>Cryptanalyst</md-table-cell><md-table-cell>Security</md-table-cell><md-table-cell numeric>$145,000</md-table-cell></md-table-row>
<md-table-row value="v3"><md-table-cell padding="checkbox"><md-checkbox aria-label="Select Grace Hopper"></md-checkbox></md-table-cell><md-table-cell head scope="row">Grace Hopper</md-table-cell><md-table-cell>Compiler Engineer</md-table-cell><md-table-cell>Platform</md-table-cell><md-table-cell numeric>$132,000</md-table-cell></md-table-row>
<md-table-row value="v4"><md-table-cell padding="checkbox"><md-checkbox aria-label="Select Linus Torvalds"></md-checkbox></md-table-cell><md-table-cell head scope="row">Linus Torvalds</md-table-cell><md-table-cell>Kernel Engineer</md-table-cell><md-table-cell>Infrastructure</md-table-cell><md-table-cell numeric>$168,000</md-table-cell></md-table-row>
<md-table-row value="v5"><md-table-cell padding="checkbox"><md-checkbox aria-label="Select Margaret Hamilton"></md-checkbox></md-table-cell><md-table-cell head scope="row">Margaret Hamilton</md-table-cell><md-table-cell>Software Lead</md-table-cell><md-table-cell>Avionics</md-table-cell><md-table-cell numeric>$175,000</md-table-cell></md-table-row>
<md-table-row value="v6"><md-table-cell padding="checkbox"><md-checkbox aria-label="Select Donald Knuth"></md-checkbox></md-table-cell><md-table-cell head scope="row">Donald Knuth</md-table-cell><md-table-cell>Author / Researcher</md-table-cell><md-table-cell>Research</md-table-cell><md-table-cell numeric>$195,000</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>import { useRef } from 'react';
import {
MdCheckbox, MdIconButton, MdTable, MdTableBody, MdTableCell, MdTableContainer,
MdTableHead, MdTableRow, MdTableToolbar,
} from '@awc-ui/react';
const STAFF = [
{ id: 'v1', name: 'Ada Lovelace', role: 'Mathematician', team: 'Engineering', salary: 120000 },
{ id: 'v2', name: 'Alan Turing', role: 'Cryptanalyst', team: 'Security', salary: 145000 },
{ id: 'v3', name: 'Grace Hopper', role: 'Compiler Engineer', team: 'Platform', salary: 132000 },
// … three more …
];
const money = (n) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 });
export function VibrantEmployees() {
const tableRef = useRef(null);
return (
// `vibrant` belongs on the CONTAINER — it tints the header band, the
// stripes and the selection colour in one go.
<MdTableContainer variant="elevated" vibrant>
<MdTableToolbar slot="top" headline="Employees" supportingText="Vibrant tonality" autoBind>
<MdIconButton slot="actions" icon="filter_list" aria-label="Filter" />
<MdIconButton slot="selection-actions" icon="delete" aria-label="Delete" />
</MdTableToolbar>
<MdTable
ref={tableRef}
selection="multiple"
striped
columnTemplate="auto 1.5fr 1.2fr 1fr 1fr"
label="Vibrant employees"
>
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head padding="checkbox">
<MdCheckbox aria-label="Select all rows" onMdChange={() => tableRef.current?.toggleSelectAll()} />
</MdTableCell>
<MdTableCell head scope="col">Name</MdTableCell>
<MdTableCell head scope="col">Role</MdTableCell>
<MdTableCell head scope="col">Team</MdTableCell>
<MdTableCell head scope="col" numeric>Salary</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
{STAFF.map((p) => (
<MdTableRow key={p.id} value={p.id}>
<MdTableCell padding="checkbox"><MdCheckbox aria-label={`Select ${p.name}`} /></MdTableCell>
<MdTableCell head scope="row">{p.name}</MdTableCell>
<MdTableCell>{p.role}</MdTableCell>
<MdTableCell>{p.team}</MdTableCell>
<MdTableCell numeric>{money(p.salary)}</MdTableCell>
</MdTableRow>
))}
</MdTableBody>
</MdTable>
</MdTableContainer>
);
}// vibrant-employees.component.ts
import { Component, CUSTOM_ELEMENTS_SCHEMA, ElementRef, ViewChild } from '@angular/core';
import { CommonModule } from '@angular/common';
import { defineCustomElements } from '@awc-ui/core/loader';
defineCustomElements(window);
interface Person { id: string; name: string; role: string; team: string; salary: number; }
@Component({
selector: 'app-vibrant-employees',
standalone: true,
imports: [CommonModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `
<!-- vibrant goes on the container; striped is a separate table flag. -->
<md-table-container variant="elevated" vibrant>
<md-table-toolbar slot="top" headline="Employees" supporting-text="Vibrant tonality" auto-bind>
<md-icon-button slot="actions" icon="filter_list" aria-label="Filter"></md-icon-button>
<md-icon-button slot="selection-actions" icon="delete" aria-label="Delete"></md-icon-button>
</md-table-toolbar>
<md-table #table selection="multiple" striped column-template="auto 1.5fr 1.2fr 1fr 1fr" label="Vibrant employees">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox">
<md-checkbox aria-label="Select all rows" (mdChange)="selectAll()"></md-checkbox>
</md-table-cell>
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col">Role</md-table-cell>
<md-table-cell head scope="col">Team</md-table-cell>
<md-table-cell head scope="col" numeric>Salary</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row *ngFor="let p of staff; trackBy: trackById" [attr.value]="p.id">
<md-table-cell padding="checkbox">
<md-checkbox [attr.aria-label]="'Select ' + p.name"></md-checkbox>
</md-table-cell>
<md-table-cell head scope="row">{{ p.name }}</md-table-cell>
<md-table-cell>{{ p.role }}</md-table-cell>
<md-table-cell>{{ p.team }}</md-table-cell>
<md-table-cell numeric>{{ p.salary | currency:'USD':'symbol':'1.0-0' }}</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>
`,
})
export class VibrantEmployeesComponent {
@ViewChild('table') tableRef!: ElementRef<HTMLElement & { toggleSelectAll(): Promise<void> }>;
staff: Person[] = [
{ id: 'v1', name: 'Ada Lovelace', role: 'Mathematician', team: 'Engineering', salary: 120000 },
{ id: 'v2', name: 'Alan Turing', role: 'Cryptanalyst', team: 'Security', salary: 145000 },
{ id: 'v3', name: 'Grace Hopper', role: 'Compiler Engineer', team: 'Platform', salary: 132000 },
];
selectAll() { void this.tableRef.nativeElement.toggleSelectAll(); }
trackById = (_: number, p: Person) => p.id;
}<script setup>
import { ref } from 'vue';
import '@awc-ui/core/define';
const STAFF = [
{ id: 'v1', name: 'Ada Lovelace', role: 'Mathematician', team: 'Engineering', salary: 120000 },
{ id: 'v2', name: 'Alan Turing', role: 'Cryptanalyst', team: 'Security', salary: 145000 },
{ id: 'v3', name: 'Grace Hopper', role: 'Compiler Engineer', team: 'Platform', salary: 132000 },
// … three more …
];
const money = (n) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 });
const table = ref(null);
</script>
<template>
<!-- vibrant goes on the container; striped is a separate table flag. -->
<md-table-container variant="elevated" vibrant>
<md-table-toolbar slot="top" headline="Employees" supporting-text="Vibrant tonality" auto-bind>
<md-icon-button slot="actions" icon="filter_list" aria-label="Filter"></md-icon-button>
<md-icon-button slot="selection-actions" icon="delete" aria-label="Delete"></md-icon-button>
</md-table-toolbar>
<md-table ref="table" selection="multiple" striped column-template="auto 1.5fr 1.2fr 1fr 1fr" label="Vibrant employees">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox">
<md-checkbox aria-label="Select all rows" @mdChange="table.toggleSelectAll()"></md-checkbox>
</md-table-cell>
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col">Role</md-table-cell>
<md-table-cell head scope="col">Team</md-table-cell>
<md-table-cell head scope="col" numeric>Salary</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row v-for="p in STAFF" :key="p.id" :value="p.id">
<md-table-cell padding="checkbox"><md-checkbox :aria-label="'Select ' + p.name"></md-checkbox></md-table-cell>
<md-table-cell head scope="row">{{ p.name }}</md-table-cell>
<md-table-cell>{{ p.role }}</md-table-cell>
<md-table-cell>{{ p.team }}</md-table-cell>
<md-table-cell numeric>{{ money(p.salary) }}</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</template><script>
import '@awc-ui/core/define';
const STAFF = [
{ id: 'v1', name: 'Ada Lovelace', role: 'Mathematician', team: 'Engineering', salary: 120000 },
{ id: 'v2', name: 'Alan Turing', role: 'Cryptanalyst', team: 'Security', salary: 145000 },
{ id: 'v3', name: 'Grace Hopper', role: 'Compiler Engineer', team: 'Platform', salary: 132000 },
// … three more …
];
const money = (n) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 });
let table;
</script>
<!-- vibrant goes on the container; striped is a separate table flag. -->
<md-table-container variant="elevated" vibrant>
<md-table-toolbar slot="top" headline="Employees" supporting-text="Vibrant tonality" auto-bind>
<md-icon-button slot="actions" icon="filter_list" aria-label="Filter"></md-icon-button>
<md-icon-button slot="selection-actions" icon="delete" aria-label="Delete"></md-icon-button>
</md-table-toolbar>
<md-table bind:this={table} selection="multiple" striped column-template="auto 1.5fr 1.2fr 1fr 1fr" label="Vibrant employees">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox">
<md-checkbox aria-label="Select all rows" on:mdChange={() => table.toggleSelectAll()}></md-checkbox>
</md-table-cell>
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col">Role</md-table-cell>
<md-table-cell head scope="col">Team</md-table-cell>
<md-table-cell head scope="col" numeric>Salary</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
{#each STAFF as p (p.id)}
<md-table-row value={p.id}>
<md-table-cell padding="checkbox"><md-checkbox aria-label={'Select ' + p.name}></md-checkbox></md-table-cell>
<md-table-cell head scope="row">{p.name}</md-table-cell>
<md-table-cell>{p.role}</md-table-cell>
<md-table-cell>{p.team}</md-table-cell>
<md-table-cell numeric>{money(p.salary)}</md-table-cell>
</md-table-row>
{/each}
</md-table-body>
</md-table>
</md-table-container>Two different mechanisms, routinely confused:
| Prop | What it does | Pair with |
|---|---|---|
sticky-header | The header stays inside the scroll region and pins to its top (classic in-flow sticky) | md-table-container[max-height] |
frozen-header | The header is lifted out of the vertical scroll area into its own grid (the header-grid part), so the scrollbar spans only the body | md-table-container[max-height] |
sticky-footer | Pins the foot row(s) to the bottom of the scroll container | md-table-foot |
max-height on the container controls the size; frozen-header controls
the architecture. Without a bounded container there is nothing to stick
within — the page scrolls instead.
In frozen mode, scrollbar picks the vertical bar’s presentation: overlay
(default) floats a thumb over the rows’ right edge and reserves no gutter;
gutter reserves a stable strip and lets the native bar render in it.
The two modes differ in what scrolls. sticky-header keeps the head inside
the scroll area and pins it there, so the container’s scrollbar spans the whole
table — including alongside the header — and a half-scrolled row can show a
sliver beneath the pinned head. frozen-header lifts the head out of the scroll
area entirely: it spans the full width, never moves, and the scrollbar is
confined to the body below it. Reach for frozen-header whenever the header
must stay visually separate from the scrolling data.
sticky-header
frozen-header
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div style="display: flex; gap: 24px; flex-wrap: wrap; align-items: flex-start;">
<div style="flex: 1 1 320px; min-inline-size: 280px;">
<p style="margin: 0 0 8px; font: var(--md-sys-typescale-label-large); color: var(--md-sys-color-on-surface-variant);"><code>sticky-header</code></p>
<md-table-container variant="outlined" max-height="212px">
<md-table column-template="2fr 1fr" sticky-header label="Sticky header">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col" numeric>Score</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell>Ada Lovelace</md-table-cell><md-table-cell numeric>98</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Grace Hopper</md-table-cell><md-table-cell numeric>96</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Alan Turing</md-table-cell><md-table-cell numeric>95</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Katherine Johnson</md-table-cell><md-table-cell numeric>94</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Margaret Hamilton</md-table-cell><md-table-cell numeric>93</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Barbara Liskov</md-table-cell><md-table-cell numeric>92</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
<div style="flex: 1 1 320px; min-inline-size: 280px;">
<p style="margin: 0 0 8px; font: var(--md-sys-typescale-label-large); color: var(--md-sys-color-on-surface-variant);"><code>frozen-header</code></p>
<md-table-container variant="outlined" max-height="212px">
<md-table column-template="2fr 1fr" frozen-header label="Frozen header">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col" numeric>Score</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell>Ada Lovelace</md-table-cell><md-table-cell numeric>98</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Grace Hopper</md-table-cell><md-table-cell numeric>96</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Alan Turing</md-table-cell><md-table-cell numeric>95</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Katherine Johnson</md-table-cell><md-table-cell numeric>94</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Margaret Hamilton</md-table-cell><md-table-cell numeric>93</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Barbara Liskov</md-table-cell><md-table-cell numeric>92</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
</div>import { MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead, MdTableRow } from '@awc-ui/react';
export function Demo() {
return (
<>
<div style={{ display: 'flex', gap: '24px', flexWrap: 'wrap', alignItems: 'flex-start' }}>
<div style={{ flex: '1 1 320px', minInlineSize: '280px' }}>
<p style={{ margin: '0 0 8px', font: 'var(--md-sys-typescale-label-large)', color: 'var(--md-sys-color-on-surface-variant)' }}><code>sticky-header</code></p>
<MdTableContainer variant="outlined" maxHeight="212px">
<MdTable columnTemplate="2fr 1fr" stickyHeader label="Sticky header">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Name</MdTableCell>
<MdTableCell head scope="col" numeric>Score</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow><MdTableCell>Ada Lovelace</MdTableCell><MdTableCell numeric>98</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell>Grace Hopper</MdTableCell><MdTableCell numeric>96</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell>Alan Turing</MdTableCell><MdTableCell numeric>95</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell>Katherine Johnson</MdTableCell><MdTableCell numeric>94</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell>Margaret Hamilton</MdTableCell><MdTableCell numeric>93</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell>Barbara Liskov</MdTableCell><MdTableCell numeric>92</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
</div>
<div style={{ flex: '1 1 320px', minInlineSize: '280px' }}>
<p style={{ margin: '0 0 8px', font: 'var(--md-sys-typescale-label-large)', color: 'var(--md-sys-color-on-surface-variant)' }}><code>frozen-header</code></p>
<MdTableContainer variant="outlined" maxHeight="212px">
<MdTable columnTemplate="2fr 1fr" frozenHeader label="Frozen header">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Name</MdTableCell>
<MdTableCell head scope="col" numeric>Score</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow><MdTableCell>Ada Lovelace</MdTableCell><MdTableCell numeric>98</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell>Grace Hopper</MdTableCell><MdTableCell numeric>96</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell>Alan Turing</MdTableCell><MdTableCell numeric>95</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell>Katherine Johnson</MdTableCell><MdTableCell numeric>94</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell>Margaret Hamilton</MdTableCell><MdTableCell numeric>93</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell>Barbara Liskov</MdTableCell><MdTableCell numeric>92</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
</div>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div style="display: flex; gap: 24px; flex-wrap: wrap; align-items: flex-start;">
<div style="flex: 1 1 320px; min-inline-size: 280px;">
<p style="margin: 0 0 8px; font: var(--md-sys-typescale-label-large); color: var(--md-sys-color-on-surface-variant);"><code>sticky-header</code></p>
<md-table-container variant="outlined" max-height="212px">
<md-table column-template="2fr 1fr" sticky-header label="Sticky header">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col" numeric>Score</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell>Ada Lovelace</md-table-cell><md-table-cell numeric>98</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Grace Hopper</md-table-cell><md-table-cell numeric>96</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Alan Turing</md-table-cell><md-table-cell numeric>95</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Katherine Johnson</md-table-cell><md-table-cell numeric>94</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Margaret Hamilton</md-table-cell><md-table-cell numeric>93</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Barbara Liskov</md-table-cell><md-table-cell numeric>92</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
<div style="flex: 1 1 320px; min-inline-size: 280px;">
<p style="margin: 0 0 8px; font: var(--md-sys-typescale-label-large); color: var(--md-sys-color-on-surface-variant);"><code>frozen-header</code></p>
<md-table-container variant="outlined" max-height="212px">
<md-table column-template="2fr 1fr" frozen-header label="Frozen header">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col" numeric>Score</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell>Ada Lovelace</md-table-cell><md-table-cell numeric>98</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Grace Hopper</md-table-cell><md-table-cell numeric>96</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Alan Turing</md-table-cell><md-table-cell numeric>95</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Katherine Johnson</md-table-cell><md-table-cell numeric>94</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Margaret Hamilton</md-table-cell><md-table-cell numeric>93</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Barbara Liskov</md-table-cell><md-table-cell numeric>92</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
</div><script setup>
import '@awc-ui/core/define';
</script>
<template>
<div style="display: flex; gap: 24px; flex-wrap: wrap; align-items: flex-start;">
<div style="flex: 1 1 320px; min-inline-size: 280px;">
<p style="margin: 0 0 8px; font: var(--md-sys-typescale-label-large); color: var(--md-sys-color-on-surface-variant);"><code>sticky-header</code></p>
<md-table-container variant="outlined" max-height="212px">
<md-table column-template="2fr 1fr" sticky-header label="Sticky header">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col" numeric>Score</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell>Ada Lovelace</md-table-cell><md-table-cell numeric>98</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Grace Hopper</md-table-cell><md-table-cell numeric>96</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Alan Turing</md-table-cell><md-table-cell numeric>95</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Katherine Johnson</md-table-cell><md-table-cell numeric>94</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Margaret Hamilton</md-table-cell><md-table-cell numeric>93</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Barbara Liskov</md-table-cell><md-table-cell numeric>92</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
<div style="flex: 1 1 320px; min-inline-size: 280px;">
<p style="margin: 0 0 8px; font: var(--md-sys-typescale-label-large); color: var(--md-sys-color-on-surface-variant);"><code>frozen-header</code></p>
<md-table-container variant="outlined" max-height="212px">
<md-table column-template="2fr 1fr" frozen-header label="Frozen header">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col" numeric>Score</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell>Ada Lovelace</md-table-cell><md-table-cell numeric>98</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Grace Hopper</md-table-cell><md-table-cell numeric>96</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Alan Turing</md-table-cell><md-table-cell numeric>95</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Katherine Johnson</md-table-cell><md-table-cell numeric>94</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Margaret Hamilton</md-table-cell><md-table-cell numeric>93</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Barbara Liskov</md-table-cell><md-table-cell numeric>92</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
</div>
</template><script>
import '@awc-ui/core/define';
</script>
<div style="display: flex; gap: 24px; flex-wrap: wrap; align-items: flex-start;">
<div style="flex: 1 1 320px; min-inline-size: 280px;">
<p style="margin: 0 0 8px; font: var(--md-sys-typescale-label-large); color: var(--md-sys-color-on-surface-variant);"><code>sticky-header</code></p>
<md-table-container variant="outlined" max-height="212px">
<md-table column-template="2fr 1fr" sticky-header label="Sticky header">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col" numeric>Score</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell>Ada Lovelace</md-table-cell><md-table-cell numeric>98</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Grace Hopper</md-table-cell><md-table-cell numeric>96</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Alan Turing</md-table-cell><md-table-cell numeric>95</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Katherine Johnson</md-table-cell><md-table-cell numeric>94</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Margaret Hamilton</md-table-cell><md-table-cell numeric>93</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Barbara Liskov</md-table-cell><md-table-cell numeric>92</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
<div style="flex: 1 1 320px; min-inline-size: 280px;">
<p style="margin: 0 0 8px; font: var(--md-sys-typescale-label-large); color: var(--md-sys-color-on-surface-variant);"><code>frozen-header</code></p>
<md-table-container variant="outlined" max-height="212px">
<md-table column-template="2fr 1fr" frozen-header label="Frozen header">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col" numeric>Score</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell>Ada Lovelace</md-table-cell><md-table-cell numeric>98</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Grace Hopper</md-table-cell><md-table-cell numeric>96</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Alan Turing</md-table-cell><md-table-cell numeric>95</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Katherine Johnson</md-table-cell><md-table-cell numeric>94</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Margaret Hamilton</md-table-cell><md-table-cell numeric>93</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Barbara Liskov</md-table-cell><md-table-cell numeric>92</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
</div><!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined" max-height="220px">
<md-table column-template="200px 140px 140px 140px 140px 140px" label="Wide table" sticky-header min-width="900px">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col" sticky="start">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Q1</md-table-cell>
<md-table-cell head scope="col" numeric>Q2</md-table-cell>
<md-table-cell head scope="col" numeric>Q3</md-table-cell>
<md-table-cell head scope="col" numeric>Q4</md-table-cell>
<md-table-cell head scope="col" numeric>Total</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="s1"><md-table-cell head scope="row" sticky="start">Acme Corp</md-table-cell><md-table-cell numeric>120</md-table-cell><md-table-cell numeric>180</md-table-cell><md-table-cell numeric>150</md-table-cell><md-table-cell numeric>210</md-table-cell><md-table-cell numeric>660</md-table-cell></md-table-row>
<md-table-row value="s2"><md-table-cell head scope="row" sticky="start">Globex</md-table-cell><md-table-cell numeric>90</md-table-cell><md-table-cell numeric>140</md-table-cell><md-table-cell numeric>170</md-table-cell><md-table-cell numeric>160</md-table-cell><md-table-cell numeric>560</md-table-cell></md-table-row>
<md-table-row value="s3"><md-table-cell head scope="row" sticky="start">Initech</md-table-cell><md-table-cell numeric>60</md-table-cell><md-table-cell numeric>80</md-table-cell><md-table-cell numeric>95</md-table-cell><md-table-cell numeric>110</md-table-cell><md-table-cell numeric>345</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>import { MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead, MdTableRow } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdTableContainer variant="outlined" maxHeight="220px">
<MdTable columnTemplate="200px 140px 140px 140px 140px 140px" label="Wide table" stickyHeader minWidth="900px">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col" sticky="start">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Q1</MdTableCell>
<MdTableCell head scope="col" numeric>Q2</MdTableCell>
<MdTableCell head scope="col" numeric>Q3</MdTableCell>
<MdTableCell head scope="col" numeric>Q4</MdTableCell>
<MdTableCell head scope="col" numeric>Total</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="s1"><MdTableCell head scope="row" sticky="start">Acme Corp</MdTableCell><MdTableCell numeric>120</MdTableCell><MdTableCell numeric>180</MdTableCell><MdTableCell numeric>150</MdTableCell><MdTableCell numeric>210</MdTableCell><MdTableCell numeric>660</MdTableCell></MdTableRow>
<MdTableRow value="s2"><MdTableCell head scope="row" sticky="start">Globex</MdTableCell><MdTableCell numeric>90</MdTableCell><MdTableCell numeric>140</MdTableCell><MdTableCell numeric>170</MdTableCell><MdTableCell numeric>160</MdTableCell><MdTableCell numeric>560</MdTableCell></MdTableRow>
<MdTableRow value="s3"><MdTableCell head scope="row" sticky="start">Initech</MdTableCell><MdTableCell numeric>60</MdTableCell><MdTableCell numeric>80</MdTableCell><MdTableCell numeric>95</MdTableCell><MdTableCell numeric>110</MdTableCell><MdTableCell numeric>345</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-table-container variant="outlined" max-height="220px">
<md-table column-template="200px 140px 140px 140px 140px 140px" label="Wide table" sticky-header min-width="900px">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col" sticky="start">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Q1</md-table-cell>
<md-table-cell head scope="col" numeric>Q2</md-table-cell>
<md-table-cell head scope="col" numeric>Q3</md-table-cell>
<md-table-cell head scope="col" numeric>Q4</md-table-cell>
<md-table-cell head scope="col" numeric>Total</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="s1"><md-table-cell head scope="row" sticky="start">Acme Corp</md-table-cell><md-table-cell numeric>120</md-table-cell><md-table-cell numeric>180</md-table-cell><md-table-cell numeric>150</md-table-cell><md-table-cell numeric>210</md-table-cell><md-table-cell numeric>660</md-table-cell></md-table-row>
<md-table-row value="s2"><md-table-cell head scope="row" sticky="start">Globex</md-table-cell><md-table-cell numeric>90</md-table-cell><md-table-cell numeric>140</md-table-cell><md-table-cell numeric>170</md-table-cell><md-table-cell numeric>160</md-table-cell><md-table-cell numeric>560</md-table-cell></md-table-row>
<md-table-row value="s3"><md-table-cell head scope="row" sticky="start">Initech</md-table-cell><md-table-cell numeric>60</md-table-cell><md-table-cell numeric>80</md-table-cell><md-table-cell numeric>95</md-table-cell><md-table-cell numeric>110</md-table-cell><md-table-cell numeric>345</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-table-container variant="outlined" max-height="220px">
<md-table column-template="200px 140px 140px 140px 140px 140px" label="Wide table" sticky-header min-width="900px">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col" sticky="start">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Q1</md-table-cell>
<md-table-cell head scope="col" numeric>Q2</md-table-cell>
<md-table-cell head scope="col" numeric>Q3</md-table-cell>
<md-table-cell head scope="col" numeric>Q4</md-table-cell>
<md-table-cell head scope="col" numeric>Total</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="s1"><md-table-cell head scope="row" sticky="start">Acme Corp</md-table-cell><md-table-cell numeric>120</md-table-cell><md-table-cell numeric>180</md-table-cell><md-table-cell numeric>150</md-table-cell><md-table-cell numeric>210</md-table-cell><md-table-cell numeric>660</md-table-cell></md-table-row>
<md-table-row value="s2"><md-table-cell head scope="row" sticky="start">Globex</md-table-cell><md-table-cell numeric>90</md-table-cell><md-table-cell numeric>140</md-table-cell><md-table-cell numeric>170</md-table-cell><md-table-cell numeric>160</md-table-cell><md-table-cell numeric>560</md-table-cell></md-table-row>
<md-table-row value="s3"><md-table-cell head scope="row" sticky="start">Initech</md-table-cell><md-table-cell numeric>60</md-table-cell><md-table-cell numeric>80</md-table-cell><md-table-cell numeric>95</md-table-cell><md-table-cell numeric>110</md-table-cell><md-table-cell numeric>345</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</template><script>
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined" max-height="220px">
<md-table column-template="200px 140px 140px 140px 140px 140px" label="Wide table" sticky-header min-width="900px">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col" sticky="start">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Q1</md-table-cell>
<md-table-cell head scope="col" numeric>Q2</md-table-cell>
<md-table-cell head scope="col" numeric>Q3</md-table-cell>
<md-table-cell head scope="col" numeric>Q4</md-table-cell>
<md-table-cell head scope="col" numeric>Total</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="s1"><md-table-cell head scope="row" sticky="start">Acme Corp</md-table-cell><md-table-cell numeric>120</md-table-cell><md-table-cell numeric>180</md-table-cell><md-table-cell numeric>150</md-table-cell><md-table-cell numeric>210</md-table-cell><md-table-cell numeric>660</md-table-cell></md-table-row>
<md-table-row value="s2"><md-table-cell head scope="row" sticky="start">Globex</md-table-cell><md-table-cell numeric>90</md-table-cell><md-table-cell numeric>140</md-table-cell><md-table-cell numeric>170</md-table-cell><md-table-cell numeric>160</md-table-cell><md-table-cell numeric>560</md-table-cell></md-table-row>
<md-table-row value="s3"><md-table-cell head scope="row" sticky="start">Initech</md-table-cell><md-table-cell numeric>60</md-table-cell><md-table-cell numeric>80</md-table-cell><md-table-cell numeric>95</md-table-cell><md-table-cell numeric>110</md-table-cell><md-table-cell numeric>345</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>Without a sticky column the same table simply scrolls as one plane — min-width
is what forces the overflow rather than letting the columns crush:
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined">
<md-table column-template="repeat(6, 160px)" label="Horizontal scroll" min-width="960px">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Q1</md-table-cell>
<md-table-cell head scope="col" numeric>Q2</md-table-cell>
<md-table-cell head scope="col" numeric>Q3</md-table-cell>
<md-table-cell head scope="col" numeric>Q4</md-table-cell>
<md-table-cell head scope="col" numeric>Total</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="h1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>120</md-table-cell><md-table-cell numeric>180</md-table-cell><md-table-cell numeric>150</md-table-cell><md-table-cell numeric>210</md-table-cell><md-table-cell numeric>660</md-table-cell></md-table-row>
<md-table-row value="h2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>90</md-table-cell><md-table-cell numeric>140</md-table-cell><md-table-cell numeric>170</md-table-cell><md-table-cell numeric>160</md-table-cell><md-table-cell numeric>560</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>import { MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead, MdTableRow } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="repeat(6, 160px)" label="Horizontal scroll" minWidth="960px">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Q1</MdTableCell>
<MdTableCell head scope="col" numeric>Q2</MdTableCell>
<MdTableCell head scope="col" numeric>Q3</MdTableCell>
<MdTableCell head scope="col" numeric>Q4</MdTableCell>
<MdTableCell head scope="col" numeric>Total</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="h1"><MdTableCell head scope="row">Acme Corp</MdTableCell><MdTableCell numeric>120</MdTableCell><MdTableCell numeric>180</MdTableCell><MdTableCell numeric>150</MdTableCell><MdTableCell numeric>210</MdTableCell><MdTableCell numeric>660</MdTableCell></MdTableRow>
<MdTableRow value="h2"><MdTableCell head scope="row">Globex</MdTableCell><MdTableCell numeric>90</MdTableCell><MdTableCell numeric>140</MdTableCell><MdTableCell numeric>170</MdTableCell><MdTableCell numeric>160</MdTableCell><MdTableCell numeric>560</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-table-container variant="outlined">
<md-table column-template="repeat(6, 160px)" label="Horizontal scroll" min-width="960px">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Q1</md-table-cell>
<md-table-cell head scope="col" numeric>Q2</md-table-cell>
<md-table-cell head scope="col" numeric>Q3</md-table-cell>
<md-table-cell head scope="col" numeric>Q4</md-table-cell>
<md-table-cell head scope="col" numeric>Total</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="h1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>120</md-table-cell><md-table-cell numeric>180</md-table-cell><md-table-cell numeric>150</md-table-cell><md-table-cell numeric>210</md-table-cell><md-table-cell numeric>660</md-table-cell></md-table-row>
<md-table-row value="h2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>90</md-table-cell><md-table-cell numeric>140</md-table-cell><md-table-cell numeric>170</md-table-cell><md-table-cell numeric>160</md-table-cell><md-table-cell numeric>560</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-table-container variant="outlined">
<md-table column-template="repeat(6, 160px)" label="Horizontal scroll" min-width="960px">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Q1</md-table-cell>
<md-table-cell head scope="col" numeric>Q2</md-table-cell>
<md-table-cell head scope="col" numeric>Q3</md-table-cell>
<md-table-cell head scope="col" numeric>Q4</md-table-cell>
<md-table-cell head scope="col" numeric>Total</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="h1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>120</md-table-cell><md-table-cell numeric>180</md-table-cell><md-table-cell numeric>150</md-table-cell><md-table-cell numeric>210</md-table-cell><md-table-cell numeric>660</md-table-cell></md-table-row>
<md-table-row value="h2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>90</md-table-cell><md-table-cell numeric>140</md-table-cell><md-table-cell numeric>170</md-table-cell><md-table-cell numeric>160</md-table-cell><md-table-cell numeric>560</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</template><script>
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined">
<md-table column-template="repeat(6, 160px)" label="Horizontal scroll" min-width="960px">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Q1</md-table-cell>
<md-table-cell head scope="col" numeric>Q2</md-table-cell>
<md-table-cell head scope="col" numeric>Q3</md-table-cell>
<md-table-cell head scope="col" numeric>Q4</md-table-cell>
<md-table-cell head scope="col" numeric>Total</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="h1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>120</md-table-cell><md-table-cell numeric>180</md-table-cell><md-table-cell numeric>150</md-table-cell><md-table-cell numeric>210</md-table-cell><md-table-cell numeric>660</md-table-cell></md-table-row>
<md-table-row value="h2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>90</md-table-cell><md-table-cell numeric>140</md-table-cell><md-table-cell numeric>170</md-table-cell><md-table-cell numeric>160</md-table-cell><md-table-cell numeric>560</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>scrollbar="overlay"
scrollbar="gutter"
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div style="display: flex; gap: 24px; flex-wrap: wrap; align-items: flex-start;">
<div style="flex: 1 1 340px; min-inline-size: 300px;">
<p style="margin: 0 0 8px; font: var(--md-sys-typescale-label-large); color: var(--md-sys-color-on-surface-variant);"><code>scrollbar="overlay"</code></p>
<md-table-container variant="outlined" max-height="212px">
<md-table frozen-header scrollbar="overlay" column-template="2fr 1fr" label="overlay scrollbar">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell>Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Initech</md-table-cell><md-table-cell numeric>$860.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Umbrella</md-table-cell><md-table-cell numeric>$2,450.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Soylent</md-table-cell><md-table-cell numeric>$1,975.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Hooli</md-table-cell><md-table-cell numeric>$4,300.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Stark Industries</md-table-cell><md-table-cell numeric>$8,120.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Cyberdyne</md-table-cell><md-table-cell numeric>$2,010.00</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
<div style="flex: 1 1 340px; min-inline-size: 300px;">
<p style="margin: 0 0 8px; font: var(--md-sys-typescale-label-large); color: var(--md-sys-color-on-surface-variant);"><code>scrollbar="gutter"</code></p>
<md-table-container variant="outlined" max-height="212px">
<md-table frozen-header scrollbar="gutter" column-template="2fr 1fr" label="gutter scrollbar">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell>Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Initech</md-table-cell><md-table-cell numeric>$860.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Umbrella</md-table-cell><md-table-cell numeric>$2,450.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Soylent</md-table-cell><md-table-cell numeric>$1,975.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Hooli</md-table-cell><md-table-cell numeric>$4,300.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Stark Industries</md-table-cell><md-table-cell numeric>$8,120.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Cyberdyne</md-table-cell><md-table-cell numeric>$2,010.00</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
</div>import { MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead, MdTableRow } from '@awc-ui/react';
export function Demo() {
return (
<>
<div style={{ display: 'flex', gap: '24px', flexWrap: 'wrap', alignItems: 'flex-start' }}>
<div style={{ flex: '1 1 340px', minInlineSize: '300px' }}>
<p style={{ margin: '0 0 8px', font: 'var(--md-sys-typescale-label-large)', color: 'var(--md-sys-color-on-surface-variant)' }}><code>scrollbar="overlay"</code></p>
<MdTableContainer variant="outlined" maxHeight="212px">
<MdTable frozenHeader scrollbar="overlay" columnTemplate="2fr 1fr" label="overlay scrollbar">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow><MdTableCell>Acme Corp</MdTableCell><MdTableCell numeric>$1,200.00</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell>Globex</MdTableCell><MdTableCell numeric>$3,120.00</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell>Initech</MdTableCell><MdTableCell numeric>$860.00</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell>Umbrella</MdTableCell><MdTableCell numeric>$2,450.00</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell>Soylent</MdTableCell><MdTableCell numeric>$1,975.00</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell>Hooli</MdTableCell><MdTableCell numeric>$4,300.00</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell>Stark Industries</MdTableCell><MdTableCell numeric>$8,120.00</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell>Cyberdyne</MdTableCell><MdTableCell numeric>$2,010.00</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
</div>
<div style={{ flex: '1 1 340px', minInlineSize: '300px' }}>
<p style={{ margin: '0 0 8px', font: 'var(--md-sys-typescale-label-large)', color: 'var(--md-sys-color-on-surface-variant)' }}><code>scrollbar="gutter"</code></p>
<MdTableContainer variant="outlined" maxHeight="212px">
<MdTable frozenHeader scrollbar="gutter" columnTemplate="2fr 1fr" label="gutter scrollbar">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow><MdTableCell>Acme Corp</MdTableCell><MdTableCell numeric>$1,200.00</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell>Globex</MdTableCell><MdTableCell numeric>$3,120.00</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell>Initech</MdTableCell><MdTableCell numeric>$860.00</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell>Umbrella</MdTableCell><MdTableCell numeric>$2,450.00</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell>Soylent</MdTableCell><MdTableCell numeric>$1,975.00</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell>Hooli</MdTableCell><MdTableCell numeric>$4,300.00</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell>Stark Industries</MdTableCell><MdTableCell numeric>$8,120.00</MdTableCell></MdTableRow>
<MdTableRow><MdTableCell>Cyberdyne</MdTableCell><MdTableCell numeric>$2,010.00</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
</div>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div style="display: flex; gap: 24px; flex-wrap: wrap; align-items: flex-start;">
<div style="flex: 1 1 340px; min-inline-size: 300px;">
<p style="margin: 0 0 8px; font: var(--md-sys-typescale-label-large); color: var(--md-sys-color-on-surface-variant);"><code>scrollbar="overlay"</code></p>
<md-table-container variant="outlined" max-height="212px">
<md-table frozen-header scrollbar="overlay" column-template="2fr 1fr" label="overlay scrollbar">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell>Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Initech</md-table-cell><md-table-cell numeric>$860.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Umbrella</md-table-cell><md-table-cell numeric>$2,450.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Soylent</md-table-cell><md-table-cell numeric>$1,975.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Hooli</md-table-cell><md-table-cell numeric>$4,300.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Stark Industries</md-table-cell><md-table-cell numeric>$8,120.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Cyberdyne</md-table-cell><md-table-cell numeric>$2,010.00</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
<div style="flex: 1 1 340px; min-inline-size: 300px;">
<p style="margin: 0 0 8px; font: var(--md-sys-typescale-label-large); color: var(--md-sys-color-on-surface-variant);"><code>scrollbar="gutter"</code></p>
<md-table-container variant="outlined" max-height="212px">
<md-table frozen-header scrollbar="gutter" column-template="2fr 1fr" label="gutter scrollbar">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell>Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Initech</md-table-cell><md-table-cell numeric>$860.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Umbrella</md-table-cell><md-table-cell numeric>$2,450.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Soylent</md-table-cell><md-table-cell numeric>$1,975.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Hooli</md-table-cell><md-table-cell numeric>$4,300.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Stark Industries</md-table-cell><md-table-cell numeric>$8,120.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Cyberdyne</md-table-cell><md-table-cell numeric>$2,010.00</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
</div><script setup>
import '@awc-ui/core/define';
</script>
<template>
<div style="display: flex; gap: 24px; flex-wrap: wrap; align-items: flex-start;">
<div style="flex: 1 1 340px; min-inline-size: 300px;">
<p style="margin: 0 0 8px; font: var(--md-sys-typescale-label-large); color: var(--md-sys-color-on-surface-variant);"><code>scrollbar="overlay"</code></p>
<md-table-container variant="outlined" max-height="212px">
<md-table frozen-header scrollbar="overlay" column-template="2fr 1fr" label="overlay scrollbar">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell>Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Initech</md-table-cell><md-table-cell numeric>$860.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Umbrella</md-table-cell><md-table-cell numeric>$2,450.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Soylent</md-table-cell><md-table-cell numeric>$1,975.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Hooli</md-table-cell><md-table-cell numeric>$4,300.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Stark Industries</md-table-cell><md-table-cell numeric>$8,120.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Cyberdyne</md-table-cell><md-table-cell numeric>$2,010.00</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
<div style="flex: 1 1 340px; min-inline-size: 300px;">
<p style="margin: 0 0 8px; font: var(--md-sys-typescale-label-large); color: var(--md-sys-color-on-surface-variant);"><code>scrollbar="gutter"</code></p>
<md-table-container variant="outlined" max-height="212px">
<md-table frozen-header scrollbar="gutter" column-template="2fr 1fr" label="gutter scrollbar">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell>Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Initech</md-table-cell><md-table-cell numeric>$860.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Umbrella</md-table-cell><md-table-cell numeric>$2,450.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Soylent</md-table-cell><md-table-cell numeric>$1,975.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Hooli</md-table-cell><md-table-cell numeric>$4,300.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Stark Industries</md-table-cell><md-table-cell numeric>$8,120.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Cyberdyne</md-table-cell><md-table-cell numeric>$2,010.00</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
</div>
</template><script>
import '@awc-ui/core/define';
</script>
<div style="display: flex; gap: 24px; flex-wrap: wrap; align-items: flex-start;">
<div style="flex: 1 1 340px; min-inline-size: 300px;">
<p style="margin: 0 0 8px; font: var(--md-sys-typescale-label-large); color: var(--md-sys-color-on-surface-variant);"><code>scrollbar="overlay"</code></p>
<md-table-container variant="outlined" max-height="212px">
<md-table frozen-header scrollbar="overlay" column-template="2fr 1fr" label="overlay scrollbar">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell>Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Initech</md-table-cell><md-table-cell numeric>$860.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Umbrella</md-table-cell><md-table-cell numeric>$2,450.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Soylent</md-table-cell><md-table-cell numeric>$1,975.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Hooli</md-table-cell><md-table-cell numeric>$4,300.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Stark Industries</md-table-cell><md-table-cell numeric>$8,120.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Cyberdyne</md-table-cell><md-table-cell numeric>$2,010.00</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
<div style="flex: 1 1 340px; min-inline-size: 300px;">
<p style="margin: 0 0 8px; font: var(--md-sys-typescale-label-large); color: var(--md-sys-color-on-surface-variant);"><code>scrollbar="gutter"</code></p>
<md-table-container variant="outlined" max-height="212px">
<md-table frozen-header scrollbar="gutter" column-template="2fr 1fr" label="gutter scrollbar">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell>Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Initech</md-table-cell><md-table-cell numeric>$860.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Umbrella</md-table-cell><md-table-cell numeric>$2,450.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Soylent</md-table-cell><md-table-cell numeric>$1,975.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Hooli</md-table-cell><md-table-cell numeric>$4,300.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Stark Industries</md-table-cell><md-table-cell numeric>$8,120.00</md-table-cell></md-table-row>
<md-table-row><md-table-cell>Cyberdyne</md-table-cell><md-table-cell numeric>$2,010.00</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
</div>Related stories: a sticky column, horizontal scroll, scrollbar modes.
Pinning is imperative: call pinColumn(index, 'start' | 'end' | 'none') with the
column’s original 0-based index (its position before any pinning). The table
re-sequences every row’s cells, maintains the sticky attributes, permutes the
column template, and emits mdPinChange. Selection and expansion state survive
— the elements just move. The move is a deliberate snap, not a FLIP.
pin-mode decides how multiple pins on one side behave:
pin-mode | Behaviour |
|---|---|
stack (default) | All pinned columns stick at the same edge, so later ones slide over earlier ones — only the top one stays fully visible |
static | Each gets a cumulative inset, so they all stay visible side-by-side (spreadsheet-style) |
setColumnVisibility(index, visible) hides or shows a column: the cells stay in
the DOM (state survives) and only their track leaves the template. The last
visible column cannot be hidden. pin-icon sets a table-wide pin glyph; a
cell’s own pin-icon wins over the cast.
const table = document.querySelector('md-table');
await table.pinColumn(0, 'start'); // freeze the first columnawait table.setColumnVisibility(3, false); // hide the fourth columnPinned headers carry a pin marker of their own — suppress it per cell with
no-pin-indicator.
<!-- index.html <head> — the icon font the components draw from -->
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&display=swap">
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div style="display: grid; gap: 12px; justify-items: start;">
<div style="display: flex; gap: 8px;">
<md-chip id="pin-stack" variant="filter" selected>pin-mode="stack"</md-chip>
<md-chip id="pin-static" variant="filter">pin-mode="static"</md-chip>
</div>
<md-table-container variant="outlined" style="inline-size: 100%;">
<md-table id="pin-table" pin-mode="stack" column-template="180px 160px 200px 160px 140px 120px" min-width="960px" label="Pinned columns">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col">Team</md-table-cell>
<md-table-cell head scope="col">Role</md-table-cell>
<md-table-cell head scope="col">Location</md-table-cell>
<md-table-cell head scope="col" numeric>Salary</md-table-cell>
<md-table-cell head scope="col">Actions</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row><md-table-cell>Ada Lovelace</md-table-cell><md-table-cell>Engineering</md-table-cell><md-table-cell>Mathematician</md-table-cell><md-table-cell>London</md-table-cell><md-table-cell numeric>$120,000</md-table-cell><md-table-cell><md-icon-button icon="edit" aria-label="Edit Ada Lovelace" density="-2"></md-icon-button><md-icon-button icon="delete" aria-label="Delete Ada Lovelace" density="-2"></md-icon-button></md-table-cell></md-table-row>
<md-table-row><md-table-cell>Alan Turing</md-table-cell><md-table-cell>Security</md-table-cell><md-table-cell>Cryptanalyst</md-table-cell><md-table-cell>Manchester</md-table-cell><md-table-cell numeric>$145,000</md-table-cell><md-table-cell><md-icon-button icon="edit" aria-label="Edit Alan Turing" density="-2"></md-icon-button><md-icon-button icon="delete" aria-label="Delete Alan Turing" density="-2"></md-icon-button></md-table-cell></md-table-row>
<md-table-row><md-table-cell>Grace Hopper</md-table-cell><md-table-cell>Platform</md-table-cell><md-table-cell>Compiler Engineer</md-table-cell><md-table-cell>New York</md-table-cell><md-table-cell numeric>$132,000</md-table-cell><md-table-cell><md-icon-button icon="edit" aria-label="Edit Grace Hopper" density="-2"></md-icon-button><md-icon-button icon="delete" aria-label="Delete Grace Hopper" density="-2"></md-icon-button></md-table-cell></md-table-row>
<md-table-row><md-table-cell>Linus Torvalds</md-table-cell><md-table-cell>Infrastructure</md-table-cell><md-table-cell>Kernel Engineer</md-table-cell><md-table-cell>Portland</md-table-cell><md-table-cell numeric>$168,000</md-table-cell><md-table-cell><md-icon-button icon="edit" aria-label="Edit Linus Torvalds" density="-2"></md-icon-button><md-icon-button icon="delete" aria-label="Delete Linus Torvalds" density="-2"></md-icon-button></md-table-cell></md-table-row>
<md-table-row><md-table-cell>Margaret Hamilton</md-table-cell><md-table-cell>Avionics</md-table-cell><md-table-cell>Software Lead</md-table-cell><md-table-cell>Boston</md-table-cell><md-table-cell numeric>$175,000</md-table-cell><md-table-cell><md-icon-button icon="edit" aria-label="Edit Margaret Hamilton" density="-2"></md-icon-button><md-icon-button icon="delete" aria-label="Delete Margaret Hamilton" density="-2"></md-icon-button></md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
<script type="module">
var table = document.getElementById('pin-table');
// Pin Name + Team to the start and Actions to the end, so the middle columns
// are what actually scrolls. Pin ONCE: pinColumn toggles, so re-running these
// three calls would UNPIN the very columns they pinned. Switching pin-mode
// does not need a re-pin — the table recomputes the offsets on its own.
table.pinColumn(0, 'start');
table.pinColumn(1, 'start');
table.pinColumn(5, 'end');
var stack = document.getElementById('pin-stack');
var stat = document.getElementById('pin-static');
var setMode = function (mode) {
table.pinMode = mode;
stack.selected = mode === 'stack';
stat.selected = mode === 'static';
};
stack.addEventListener('mdSelect', function () { setMode('stack'); });
stat.addEventListener('mdSelect', function () { setMode('static'); });
</script>import { useEffect, useRef, useState } from 'react';
import {
MdChip, MdIconButton, MdTable, MdTableBody, MdTableCell, MdTableContainer,
MdTableHead, MdTableRow,
} from '@awc-ui/react';
const PEOPLE = [
{ name: 'Ada Lovelace', team: 'Engineering', role: 'Mathematician', location: 'London', salary: 120000 },
{ name: 'Alan Turing', team: 'Security', role: 'Cryptanalyst', location: 'Manchester', salary: 145000 },
{ name: 'Grace Hopper', team: 'Platform', role: 'Compiler Engineer', location: 'New York', salary: 132000 },
{ name: 'Linus Torvalds', team: 'Infrastructure', role: 'Kernel Engineer', location: 'Portland', salary: 168000 },
{ name: 'Margaret Hamilton', team: 'Avionics', role: 'Software Lead', location: 'Boston', salary: 175000 },
];
const money = (n) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 });
export function PinnedColumns() {
const tableRef = useRef(null);
const [mode, setMode] = useState('stack');
// pinColumn is a method, not a prop — call it once the element exists, and
// ONLY once: it toggles, so a second identical call unpins. That is why this
// effect has an empty dep array and deliberately does NOT re-run on `mode`;
// changing pinMode recomputes the sticky offsets inside the component.
useEffect(() => {
const table = tableRef.current;
if (!table) return;
void (async () => {
await table.pinColumn(0, 'start');
await table.pinColumn(1, 'start');
await table.pinColumn(5, 'end'); // side 'none' unpins, as does re-passing 'end'
})();
}, []);
return (
<>
<div style={{ display: 'flex', gap: 8 }}>
<MdChip variant="filter" selected={mode === 'stack'} onMdSelect={() => setMode('stack')}>
pin-mode="stack"
</MdChip>
<MdChip variant="filter" selected={mode === 'static'} onMdSelect={() => setMode('static')}>
pin-mode="static"
</MdChip>
</div>
<MdTableContainer variant="outlined">
{/* pinColumn needs an explicit column-template (no repeat()) and one
cell per column in every row. */}
<MdTable
ref={tableRef}
pinMode={mode}
columnTemplate="180px 160px 200px 160px 140px 120px"
minWidth="960px"
label="Pinned columns"
onMdPinChange={(e) => console.log('pin:', e.detail)}
>
<MdTableHead>
<MdTableRow rowgroup="head">
{['Name', 'Team', 'Role', 'Location'].map((h) => (
<MdTableCell key={h} head scope="col">{h}</MdTableCell>
))}
<MdTableCell head scope="col" numeric>Salary</MdTableCell>
<MdTableCell head scope="col">Actions</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
{PEOPLE.map((p) => (
<MdTableRow key={p.name}>
<MdTableCell>{p.name}</MdTableCell>
<MdTableCell>{p.team}</MdTableCell>
<MdTableCell>{p.role}</MdTableCell>
<MdTableCell>{p.location}</MdTableCell>
<MdTableCell numeric>{money(p.salary)}</MdTableCell>
<MdTableCell>
<MdIconButton icon="edit" aria-label={`Edit ${p.name}`} density={-2} />
<MdIconButton icon="delete" aria-label={`Delete ${p.name}`} density={-2} />
</MdTableCell>
</MdTableRow>
))}
</MdTableBody>
</MdTable>
</MdTableContainer>
</>
);
}// pinned-columns.component.ts
import { AfterViewInit, Component, CUSTOM_ELEMENTS_SCHEMA, ElementRef, ViewChild } from '@angular/core';
import { CommonModule } from '@angular/common';
import { defineCustomElements } from '@awc-ui/core/loader';
defineCustomElements(window);
type PinSide = 'start' | 'end' | 'none';
type PinnableTable = HTMLElement & { pinMode: string; pinColumn(i: number, side: PinSide): Promise<unknown> };
@Component({
selector: 'app-pinned-columns',
standalone: true,
imports: [CommonModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `
<div style="display: flex; gap: 8px;">
<md-chip variant="filter" [attr.selected]="mode === 'stack' ? '' : null" (mdSelect)="setMode('stack')">
pin-mode="stack"
</md-chip>
<md-chip variant="filter" [attr.selected]="mode === 'static' ? '' : null" (mdSelect)="setMode('static')">
pin-mode="static"
</md-chip>
</div>
<md-table-container variant="outlined">
<md-table
#table
[attr.pin-mode]="mode"
column-template="180px 160px 200px 160px 140px 120px"
min-width="960px"
label="Pinned columns"
>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col">Team</md-table-cell>
<md-table-cell head scope="col">Role</md-table-cell>
<md-table-cell head scope="col">Location</md-table-cell>
<md-table-cell head scope="col" numeric>Salary</md-table-cell>
<md-table-cell head scope="col">Actions</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row *ngFor="let p of people; trackBy: trackByName">
<md-table-cell>{{ p.name }}</md-table-cell>
<md-table-cell>{{ p.team }}</md-table-cell>
<md-table-cell>{{ p.role }}</md-table-cell>
<md-table-cell>{{ p.location }}</md-table-cell>
<md-table-cell numeric>{{ p.salary | currency:'USD':'symbol':'1.0-0' }}</md-table-cell>
<md-table-cell>
<md-icon-button icon="edit" [attr.aria-label]="'Edit ' + p.name" density="-2"></md-icon-button>
<md-icon-button icon="delete" [attr.aria-label]="'Delete ' + p.name" density="-2"></md-icon-button>
</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>
`,
})
export class PinnedColumnsComponent implements AfterViewInit {
@ViewChild('table') tableRef!: ElementRef<PinnableTable>;
mode: 'stack' | 'static' = 'stack';
people = [
{ name: 'Ada Lovelace', team: 'Engineering', role: 'Mathematician', location: 'London', salary: 120000 },
{ name: 'Alan Turing', team: 'Security', role: 'Cryptanalyst', location: 'Manchester', salary: 145000 },
{ name: 'Grace Hopper', team: 'Platform', role: 'Compiler Engineer', location: 'New York', salary: 132000 },
{ name: 'Linus Torvalds', team: 'Infrastructure', role: 'Kernel Engineer', location: 'Portland', salary: 168000 },
{ name: 'Margaret Hamilton', team: 'Avionics', role: 'Software Lead', location: 'Boston', salary: 175000 },
];
ngAfterViewInit() { void this.applyPins(); }
setMode(mode: 'stack' | 'static') {
// No re-pin here on purpose: pinColumn toggles, so calling applyPins()
// again would unpin all three columns. The table recomputes the sticky
// offsets itself when pin-mode changes.
this.mode = mode;
}
/** Called exactly once, after the view exists. */
private async applyPins() {
const table = this.tableRef.nativeElement;
await table.pinColumn(0, 'start');
await table.pinColumn(1, 'start');
await table.pinColumn(5, 'end');
}
trackByName = (_: number, p: { name: string }) => p.name;
}<script setup>
import { onMounted, ref } from 'vue';
import '@awc-ui/core/define';
const PEOPLE = [
{ name: 'Ada Lovelace', team: 'Engineering', role: 'Mathematician', location: 'London', salary: 120000 },
{ name: 'Alan Turing', team: 'Security', role: 'Cryptanalyst', location: 'Manchester', salary: 145000 },
{ name: 'Grace Hopper', team: 'Platform', role: 'Compiler Engineer', location: 'New York', salary: 132000 },
{ name: 'Linus Torvalds', team: 'Infrastructure', role: 'Kernel Engineer', location: 'Portland', salary: 168000 },
{ name: 'Margaret Hamilton', team: 'Avionics', role: 'Software Lead', location: 'Boston', salary: 175000 },
];
const money = (n) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 });
const table = ref(null);
const mode = ref('stack');
// pinColumn is a method — call it after mount, and ONLY then. It toggles, so
// re-pinning on a mode change would unpin all three columns; pin-mode changes
// recompute the sticky offsets inside the component with no help from us.
const applyPins = async () => {
await table.value.pinColumn(0, 'start');
await table.value.pinColumn(1, 'start');
await table.value.pinColumn(5, 'end'); // 'none' unpins — and so does a second 'end'
};
onMounted(applyPins);
</script>
<template>
<div style="display: flex; gap: 8px;">
<md-chip variant="filter" :selected="mode === 'stack'" @mdSelect="mode = 'stack'">pin-mode="stack"</md-chip>
<md-chip variant="filter" :selected="mode === 'static'" @mdSelect="mode = 'static'">pin-mode="static"</md-chip>
</div>
<md-table-container variant="outlined">
<md-table
ref="table"
:pin-mode="mode"
column-template="180px 160px 200px 160px 140px 120px"
min-width="960px"
label="Pinned columns"
@mdPinChange="console.log('pin:', $event.detail)"
>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col">Team</md-table-cell>
<md-table-cell head scope="col">Role</md-table-cell>
<md-table-cell head scope="col">Location</md-table-cell>
<md-table-cell head scope="col" numeric>Salary</md-table-cell>
<md-table-cell head scope="col">Actions</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row v-for="p in PEOPLE" :key="p.name">
<md-table-cell>{{ p.name }}</md-table-cell>
<md-table-cell>{{ p.team }}</md-table-cell>
<md-table-cell>{{ p.role }}</md-table-cell>
<md-table-cell>{{ p.location }}</md-table-cell>
<md-table-cell numeric>{{ money(p.salary) }}</md-table-cell>
<md-table-cell>
<md-icon-button icon="edit" :aria-label="'Edit ' + p.name" density="-2"></md-icon-button>
<md-icon-button icon="delete" :aria-label="'Delete ' + p.name" density="-2"></md-icon-button>
</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</template><script>
import { onMount } from 'svelte';
import '@awc-ui/core/define';
const PEOPLE = [
{ name: 'Ada Lovelace', team: 'Engineering', role: 'Mathematician', location: 'London', salary: 120000 },
{ name: 'Alan Turing', team: 'Security', role: 'Cryptanalyst', location: 'Manchester', salary: 145000 },
{ name: 'Grace Hopper', team: 'Platform', role: 'Compiler Engineer', location: 'New York', salary: 132000 },
{ name: 'Linus Torvalds', team: 'Infrastructure', role: 'Kernel Engineer', location: 'Portland', salary: 168000 },
{ name: 'Margaret Hamilton', team: 'Avionics', role: 'Software Lead', location: 'Boston', salary: 175000 },
];
const money = (n) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 });
let table;
let mode = 'stack';
// pinColumn is a method — call it after mount, once. It toggles, so a second
// identical call unpins; and pin-mode changes recompute the sticky offsets
// inside the component, so there is nothing to re-pin.
const applyPins = async () => {
await table.pinColumn(0, 'start');
await table.pinColumn(1, 'start');
await table.pinColumn(5, 'end'); // 'none' unpins — and so does a second 'end'
};
const setMode = (next) => { mode = next; };
onMount(applyPins);
</script>
<div style="display: flex; gap: 8px;">
<md-chip variant="filter" selected={mode === 'stack'} on:mdSelect={() => setMode('stack')}>pin-mode="stack"</md-chip>
<md-chip variant="filter" selected={mode === 'static'} on:mdSelect={() => setMode('static')}>pin-mode="static"</md-chip>
</div>
<md-table-container variant="outlined">
<md-table
bind:this={table}
pin-mode={mode}
column-template="180px 160px 200px 160px 140px 120px"
min-width="960px"
label="Pinned columns"
on:mdPinChange={(e) => console.log('pin:', e.detail)}
>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col">Team</md-table-cell>
<md-table-cell head scope="col">Role</md-table-cell>
<md-table-cell head scope="col">Location</md-table-cell>
<md-table-cell head scope="col" numeric>Salary</md-table-cell>
<md-table-cell head scope="col">Actions</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
{#each PEOPLE as p (p.name)}
<md-table-row>
<md-table-cell>{p.name}</md-table-cell>
<md-table-cell>{p.team}</md-table-cell>
<md-table-cell>{p.role}</md-table-cell>
<md-table-cell>{p.location}</md-table-cell>
<md-table-cell numeric>{money(p.salary)}</md-table-cell>
<md-table-cell>
<md-icon-button icon="edit" aria-label={'Edit ' + p.name} density="-2"></md-icon-button>
<md-icon-button icon="delete" aria-label={'Delete ' + p.name} density="-2"></md-icon-button>
</md-table-cell>
</md-table-row>
{/each}
</md-table-body>
</md-table>
</md-table-container>A md-table-sort-label inside a header cell
renders the arrow (3-state: asc → desc → none) and asks the table to change
sort; the table pushes the new state down to every sort label and emits
mdSortChange.
Call animateNextChange() before you re-render the sorted rows — it
snapshots the current row boxes and FLIP-animates whatever the next
synchronous DOM mutation does to them. Re-render without it and the reorder
animation is lost. (Sort and pagination driven through the table’s own events
are bracketed automatically; custom filters, async refetches and any other
out-of-band reorder need the explicit call. Async mutations settle as a clean
no-op.)
motion="expressive" is the default; set motion="none" to opt out (e.g. when
you drive your own FLIP wiring). prefers-reduced-motion always wins.
table.addEventListener('mdSortChange', (e) => { table.animateNextChange(); // opt into the reorder animation renderRows(sortData(e.detail)); // YOU sort});setSort(column, order) sets the state programmatically without a click.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined">
<md-table id="sortable" column-template="2fr 1fr 1fr" label="Sortable invoices">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col"><md-table-sort-label column="client">Client</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col" numeric><md-table-sort-label column="amount">Amount</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col"><md-table-sort-label column="status">Status</md-table-sort-label></md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body id="sortable-body">
<md-table-row value="r1" data-idx="0"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2" data-idx="1"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
<md-table-row value="r3" data-idx="2"><md-table-cell head scope="row">Initech</md-table-cell><md-table-cell numeric>$860.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r4" data-idx="3"><md-table-cell head scope="row">Umbrella</md-table-cell><md-table-cell numeric>$2,450.00</md-table-cell><md-table-cell>Draft</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<script type="module">
var COLS = { client: 0, amount: 1, status: 2 };
var body = document.getElementById('sortable-body');
document.getElementById('sortable').addEventListener('mdSortChange', function (e) {
var column = e.detail.column;
var order = e.detail.order;
var rows = Array.prototype.slice.call(body.querySelectorAll('md-table-row'));
rows.sort(function (a, b) {
// order 'none' is the third click — restore the authored order.
if (order === 'none' || !(column in COLS)) return a.dataset.idx - b.dataset.idx;
var i = COLS[column];
var av = (a.children[i].textContent || '').trim();
var bv = (b.children[i].textContent || '').trim();
var cmp = column === 'amount'
? parseFloat(av.replace(/[^0-9.-]/g, '')) - parseFloat(bv.replace(/[^0-9.-]/g, ''))
: av.localeCompare(bv);
return order === 'desc' ? -cmp : cmp;
});
rows.forEach(function (r) { body.appendChild(r); });
});
</script>import { useMemo, useState } from 'react';
import {
MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead, MdTableRow, MdTableSortLabel,
} from '@awc-ui/react';
const INVOICES = [
{ id: 'r1', client: 'Acme Corp', amount: 1200, status: 'Paid' },
{ id: 'r2', client: 'Globex', amount: 3120, status: 'Pending' },
{ id: 'r3', client: 'Initech', amount: 860, status: 'Paid' },
{ id: 'r4', client: 'Umbrella', amount: 2450, status: 'Draft' },
];
const money = (n) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
export function SortableInvoices() {
const [sort, setSort] = useState({ column: '', order: 'none' });
const rows = useMemo(() => {
if (!sort.column || sort.order === 'none') return INVOICES;
const dir = sort.order === 'desc' ? -1 : 1;
return [...INVOICES].sort((a, b) => {
const av = a[sort.column];
const bv = b[sort.column];
return (typeof av === 'number' ? av - bv : String(av).localeCompare(String(bv))) * dir;
});
}, [sort]);
return (
<MdTableContainer variant="outlined">
{/* md-table tracks which column is active and which way it points; the
event just tells you what to sort by. */}
<MdTable
columnTemplate="2fr 1fr 1fr"
label="Sortable invoices"
onMdSortChange={(e) => setSort(e.detail)}
>
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col"><MdTableSortLabel column="client">Client</MdTableSortLabel></MdTableCell>
<MdTableCell head scope="col" numeric><MdTableSortLabel column="amount">Amount</MdTableSortLabel></MdTableCell>
<MdTableCell head scope="col"><MdTableSortLabel column="status">Status</MdTableSortLabel></MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
{rows.map((row) => (
<MdTableRow key={row.id} value={row.id}>
<MdTableCell head scope="row">{row.client}</MdTableCell>
<MdTableCell numeric>{money(row.amount)}</MdTableCell>
<MdTableCell>{row.status}</MdTableCell>
</MdTableRow>
))}
</MdTableBody>
</MdTable>
</MdTableContainer>
);
}// sortable-invoices.component.ts
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { CommonModule } from '@angular/common';
import { defineCustomElements } from '@awc-ui/core/loader';
defineCustomElements(window);
interface Invoice { id: string; client: string; amount: number; status: string; }
@Component({
selector: 'app-sortable-invoices',
standalone: true,
imports: [CommonModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `
<md-table-container variant="outlined">
<md-table
column-template="2fr 1fr 1fr"
label="Sortable invoices"
(mdSortChange)="onSort($event)"
>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col"><md-table-sort-label column="client">Client</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col" numeric><md-table-sort-label column="amount">Amount</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col"><md-table-sort-label column="status">Status</md-table-sort-label></md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row *ngFor="let row of sorted; trackBy: trackById" [attr.value]="row.id">
<md-table-cell head scope="row">{{ row.client }}</md-table-cell>
<md-table-cell numeric>{{ row.amount | currency }}</md-table-cell>
<md-table-cell>{{ row.status }}</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>
`,
})
export class SortableInvoicesComponent {
private readonly invoices: Invoice[] = [
{ id: 'r1', client: 'Acme Corp', amount: 1200, status: 'Paid' },
{ id: 'r2', client: 'Globex', amount: 3120, status: 'Pending' },
{ id: 'r3', client: 'Initech', amount: 860, status: 'Paid' },
{ id: 'r4', client: 'Umbrella', amount: 2450, status: 'Draft' },
];
sorted = this.invoices;
onSort(event: Event) {
const { column, order } = (event as CustomEvent<{ column: string; order: string }>).detail;
if (!column || order === 'none') {
this.sorted = this.invoices;
return;
}
const dir = order === 'desc' ? -1 : 1;
this.sorted = [...this.invoices].sort((a, b) => {
const av = a[column as keyof Invoice];
const bv = b[column as keyof Invoice];
return (typeof av === 'number' && typeof bv === 'number'
? av - bv
: String(av).localeCompare(String(bv))) * dir;
});
}
trackById = (_: number, row: Invoice) => row.id;
}<script setup>
import { computed, ref } from 'vue';
import '@awc-ui/core/define';
const INVOICES = [
{ id: 'r1', client: 'Acme Corp', amount: 1200, status: 'Paid' },
{ id: 'r2', client: 'Globex', amount: 3120, status: 'Pending' },
{ id: 'r3', client: 'Initech', amount: 860, status: 'Paid' },
{ id: 'r4', client: 'Umbrella', amount: 2450, status: 'Draft' },
];
const money = (n) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
const sort = ref({ column: '', order: 'none' });
const rows = computed(() => {
if (!sort.value.column || sort.value.order === 'none') return INVOICES;
const dir = sort.value.order === 'desc' ? -1 : 1;
return [...INVOICES].sort((a, b) => {
const av = a[sort.value.column];
const bv = b[sort.value.column];
return (typeof av === 'number' ? av - bv : String(av).localeCompare(String(bv))) * dir;
});
});
</script>
<template>
<md-table-container variant="outlined">
<md-table
column-template="2fr 1fr 1fr"
label="Sortable invoices"
@mdSortChange="sort = $event.detail"
>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col"><md-table-sort-label column="client">Client</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col" numeric><md-table-sort-label column="amount">Amount</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col"><md-table-sort-label column="status">Status</md-table-sort-label></md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row v-for="row in rows" :key="row.id" :value="row.id">
<md-table-cell head scope="row">{{ row.client }}</md-table-cell>
<md-table-cell numeric>{{ money(row.amount) }}</md-table-cell>
<md-table-cell>{{ row.status }}</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</template><script>
import '@awc-ui/core/define';
const INVOICES = [
{ id: 'r1', client: 'Acme Corp', amount: 1200, status: 'Paid' },
{ id: 'r2', client: 'Globex', amount: 3120, status: 'Pending' },
{ id: 'r3', client: 'Initech', amount: 860, status: 'Paid' },
{ id: 'r4', client: 'Umbrella', amount: 2450, status: 'Draft' },
];
const money = (n) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
let sort = { column: '', order: 'none' };
$: rows = !sort.column || sort.order === 'none'
? INVOICES
: [...INVOICES].sort((a, b) => {
const dir = sort.order === 'desc' ? -1 : 1;
const av = a[sort.column];
const bv = b[sort.column];
return (typeof av === 'number' ? av - bv : String(av).localeCompare(String(bv))) * dir;
});
</script>
<md-table-container variant="outlined">
<md-table
column-template="2fr 1fr 1fr"
label="Sortable invoices"
on:mdSortChange={(e) => (sort = e.detail)}
>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col"><md-table-sort-label column="client">Client</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col" numeric><md-table-sort-label column="amount">Amount</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col"><md-table-sort-label column="status">Status</md-table-sort-label></md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
{#each rows as row (row.id)}
<md-table-row value={row.id}>
<md-table-cell head scope="row">{row.client}</md-table-cell>
<md-table-cell numeric>{money(row.amount)}</md-table-cell>
<md-table-cell>{row.status}</md-table-cell>
</md-table-row>
{/each}
</md-table-body>
</md-table>
</md-table-container>The sortable story wires the full round-trip — click a header, reorder your own array, feed it back.
selection | Behaviour |
|---|---|
none (default) | No selection |
single | Radio-like — selecting a row deselects every other; selectAll() and the select-all checkbox are inert |
multiple | Checkbox-like — select-all checkbox plus selectAll() / deselectAll() / toggleSelectAll() |
Give each selectable row a value — that is what comes back in the selection
state. Mark totals rows selectable="false" so selectAll() skips them.
You author the checkboxes; the table handles them. Slot a md-checkbox into
a padding="checkbox" cell in each row, and one more into the head row for the
select-all box. From there the table takes over: it listens for their mdChange
itself, selects the row (or runs select-all / deselect-all from the head box),
and keeps that box’s indeterminate “some but not all” state in sync. Plain
markup works with zero wiring. The handling is deliberately declarative — it
applies the checkbox’s end state rather than toggling — so a handler of your own
calling toggleSelectAll() on top of it converges to the same result rather than
cancelling out; it is simply redundant. Call toggleSelectAll() when something
other than the select-all checkbox drives select-all: a toolbar button, a menu
item, a keyboard shortcut.
const { count, total, values, all, indeterminate } = await table.getSelection();mdSelectionChange carries the same MdTableSelectionState shape on every
change.
<!-- index.html <head> — the icon font the components draw from -->
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&display=swap">
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined">
<md-table-toolbar slot="top" headline="Invoices" supporting-text="Select rows to act on them" auto-bind>
<md-icon-button slot="actions" icon="filter_list" aria-label="Filter"></md-icon-button>
<md-icon-button slot="selection-actions" icon="delete" aria-label="Delete"></md-icon-button>
<md-icon-button slot="selection-actions" icon="archive" aria-label="Archive"></md-icon-button>
</md-table-toolbar>
<md-table id="selectable" column-template="auto 2fr 1fr 1fr" label="Selectable invoices" selection="multiple">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox"><md-checkbox id="select-all" aria-label="Select all rows"></md-checkbox></md-table-cell>
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell padding="checkbox"><md-checkbox aria-label="Select Acme Corp"></md-checkbox></md-table-cell><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell padding="checkbox"><md-checkbox aria-label="Select Globex"></md-checkbox></md-table-cell><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
<md-table-row value="r3"><md-table-cell padding="checkbox"><md-checkbox aria-label="Select Initech"></md-checkbox></md-table-cell><md-table-cell head scope="row">Initech</md-table-cell><md-table-cell numeric>$860.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<script type="module">
import '@awc-ui/core/define';
const table = document.getElementById('selectable');
// No checkbox wiring here on purpose. You AUTHOR the checkboxes (the table
// does not generate them), but with selection="multiple" the table handles
// them: a slotted md-checkbox in a body row selects that row, and one in the
// head acts as the select-all box — selectAll / deselectAll, plus the
// indeterminate ("some but not all") state. Only reach for toggleSelectAll()
// when something OTHER than the select-all checkbox drives it (a menu item, a
// keyboard shortcut).
// auto-bind on the toolbar makes it follow this event into selection mode;
// without it, read event.detail.values yourself.
table.addEventListener('mdSelectionChange', (e) => {
console.log('selected rows:', e.detail.values);
});
</script>import { useRef, useState } from 'react';
import {
MdCheckbox, MdIconButton, MdTable, MdTableBody, MdTableCell, MdTableContainer,
MdTableHead, MdTableRow, MdTableToolbar,
} from '@awc-ui/react';
const INVOICES = [
{ id: 'r1', client: 'Acme Corp', amount: 1200, status: 'Paid' },
{ id: 'r2', client: 'Globex', amount: 3120, status: 'Pending' },
{ id: 'r3', client: 'Initech', amount: 860, status: 'Paid' },
];
const money = (n) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
export function SelectableInvoices() {
const tableRef = useRef(null);
const [selected, setSelected] = useState([]);
return (
<MdTableContainer variant="outlined">
{/* autoBind makes the toolbar switch to selection mode on its own. */}
<MdTableToolbar slot="top" headline="Invoices" supportingText="Select rows to act on them" autoBind>
<MdIconButton slot="actions" icon="filter_list" aria-label="Filter" />
<MdIconButton slot="selection-actions" icon="delete" aria-label="Delete" />
<MdIconButton slot="selection-actions" icon="archive" aria-label="Archive" />
</MdTableToolbar>
<MdTable
ref={tableRef}
columnTemplate="auto 2fr 1fr 1fr"
label="Selectable invoices"
selection="multiple"
onMdSelectionChange={(e) => setSelected(e.detail.values)}
>
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head padding="checkbox">
{/* OPTIONAL: the table already handles a slotted head checkbox
as the select-all box. This explicit handler is kept only to show
the imperative route; the handling is declarative, so the two
converge instead of cancelling. Delete it and select-all still
works. */}
<MdCheckbox aria-label="Select all rows" onMdChange={() => tableRef.current?.toggleSelectAll()} />
</MdTableCell>
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
{INVOICES.map((row) => (
<MdTableRow key={row.id} value={row.id}>
<MdTableCell padding="checkbox"><MdCheckbox aria-label={`Select ${row.client}`} /></MdTableCell>
<MdTableCell head scope="row">{row.client}</MdTableCell>
<MdTableCell numeric>{money(row.amount)}</MdTableCell>
<MdTableCell>{row.status}</MdTableCell>
</MdTableRow>
))}
</MdTableBody>
</MdTable>
</MdTableContainer>
);
}// selectable-invoices.component.ts
import { Component, CUSTOM_ELEMENTS_SCHEMA, ElementRef, ViewChild } from '@angular/core';
import { CommonModule } from '@angular/common';
import { defineCustomElements } from '@awc-ui/core/loader';
defineCustomElements(window);
interface Invoice { id: string; client: string; amount: number; status: string; }
@Component({
selector: 'app-selectable-invoices',
standalone: true,
imports: [CommonModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `
<md-table-container variant="outlined">
<md-table-toolbar slot="top" headline="Invoices" supporting-text="Select rows to act on them" auto-bind>
<md-icon-button slot="actions" icon="filter_list" aria-label="Filter"></md-icon-button>
<md-icon-button slot="selection-actions" icon="delete" aria-label="Delete"></md-icon-button>
<md-icon-button slot="selection-actions" icon="archive" aria-label="Archive"></md-icon-button>
</md-table-toolbar>
<md-table
#table
column-template="auto 2fr 1fr 1fr"
label="Selectable invoices"
selection="multiple"
(mdSelectionChange)="selected = $any($event).detail.values"
>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox">
<md-checkbox aria-label="Select all rows" (mdChange)="selectAll()"></md-checkbox>
</md-table-cell>
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row *ngFor="let row of invoices; trackBy: trackById" [attr.value]="row.id">
<md-table-cell padding="checkbox">
<md-checkbox [attr.aria-label]="'Select ' + row.client"></md-checkbox>
</md-table-cell>
<md-table-cell head scope="row">{{ row.client }}</md-table-cell>
<md-table-cell numeric>{{ row.amount | currency }}</md-table-cell>
<md-table-cell>{{ row.status }}</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>
`,
})
export class SelectableInvoicesComponent {
@ViewChild('table') tableRef!: ElementRef<HTMLElement & { toggleSelectAll(): Promise<void> }>;
invoices: Invoice[] = [
{ id: 'r1', client: 'Acme Corp', amount: 1200, status: 'Paid' },
{ id: 'r2', client: 'Globex', amount: 3120, status: 'Pending' },
{ id: 'r3', client: 'Initech', amount: 860, status: 'Paid' },
];
selected: string[] = [];
selectAll() {
void this.tableRef.nativeElement.toggleSelectAll();
}
trackById = (_: number, row: Invoice) => row.id;
}<script setup>
import { ref } from 'vue';
import '@awc-ui/core/define';
const INVOICES = [
{ id: 'r1', client: 'Acme Corp', amount: 1200, status: 'Paid' },
{ id: 'r2', client: 'Globex', amount: 3120, status: 'Pending' },
{ id: 'r3', client: 'Initech', amount: 860, status: 'Paid' },
];
const money = (n) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
const table = ref(null);
const selected = ref([]);
</script>
<template>
<md-table-container variant="outlined">
<md-table-toolbar slot="top" headline="Invoices" supporting-text="Select rows to act on them" auto-bind>
<md-icon-button slot="actions" icon="filter_list" aria-label="Filter"></md-icon-button>
<md-icon-button slot="selection-actions" icon="delete" aria-label="Delete"></md-icon-button>
<md-icon-button slot="selection-actions" icon="archive" aria-label="Archive"></md-icon-button>
</md-table-toolbar>
<md-table
ref="table"
column-template="auto 2fr 1fr 1fr"
label="Selectable invoices"
selection="multiple"
@mdSelectionChange="selected = $event.detail.values"
>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox">
<md-checkbox aria-label="Select all rows" @mdChange="table.toggleSelectAll()"></md-checkbox>
</md-table-cell>
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row v-for="row in INVOICES" :key="row.id" :value="row.id">
<md-table-cell padding="checkbox">
<md-checkbox :aria-label="'Select ' + row.client"></md-checkbox>
</md-table-cell>
<md-table-cell head scope="row">{{ row.client }}</md-table-cell>
<md-table-cell numeric>{{ money(row.amount) }}</md-table-cell>
<md-table-cell>{{ row.status }}</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</template><script>
import '@awc-ui/core/define';
const INVOICES = [
{ id: 'r1', client: 'Acme Corp', amount: 1200, status: 'Paid' },
{ id: 'r2', client: 'Globex', amount: 3120, status: 'Pending' },
{ id: 'r3', client: 'Initech', amount: 860, status: 'Paid' },
];
const money = (n) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
let table;
let selected = [];
</script>
<md-table-container variant="outlined">
<md-table-toolbar slot="top" headline="Invoices" supporting-text="Select rows to act on them" auto-bind>
<md-icon-button slot="actions" icon="filter_list" aria-label="Filter"></md-icon-button>
<md-icon-button slot="selection-actions" icon="delete" aria-label="Delete"></md-icon-button>
<md-icon-button slot="selection-actions" icon="archive" aria-label="Archive"></md-icon-button>
</md-table-toolbar>
<md-table
bind:this={table}
column-template="auto 2fr 1fr 1fr"
label="Selectable invoices"
selection="multiple"
on:mdSelectionChange={(e) => (selected = e.detail.values)}
>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox">
<md-checkbox aria-label="Select all rows" on:mdChange={() => table.toggleSelectAll()}></md-checkbox>
</md-table-cell>
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
{#each INVOICES as row (row.id)}
<md-table-row value={row.id}>
<md-table-cell padding="checkbox">
<md-checkbox aria-label={'Select ' + row.client}></md-checkbox>
</md-table-cell>
<md-table-cell head scope="row">{row.client}</md-table-cell>
<md-table-cell numeric>{money(row.amount)}</md-table-cell>
<md-table-cell>{row.status}</md-table-cell>
</md-table-row>
{/each}
</md-table-body>
</md-table>
</md-table-container><!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined">
<md-table column-template="auto 2fr 1fr 1fr" label="Expandable invoices" keep-height="false">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox"></md-table-cell>
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="e1" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Acme Corp"></md-table-expand-toggle></md-table-cell>
<md-table-cell head scope="row">Acme Corp</md-table-cell>
<md-table-cell numeric>$1,200.00</md-table-cell>
<md-table-cell>Paid</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Three line items · paid by card ending 4242 on 4 May.</div>
</md-table-row>
<md-table-row value="e2" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Globex"></md-table-expand-toggle></md-table-cell>
<md-table-cell head scope="row">Globex</md-table-cell>
<md-table-cell numeric>$3,120.00</md-table-cell>
<md-table-cell>Pending</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Awaiting PO approval — chased 2 days ago.</div>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>import { MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableExpandToggle, MdTableHead, MdTableRow } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="auto 2fr 1fr 1fr" label="Expandable invoices" keepHeight="false">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head padding="checkbox"></MdTableCell>
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="e1" expandable>
<MdTableCell padding="checkbox"><MdTableExpandToggle buttonLabel="Expand details for Acme Corp"></MdTableExpandToggle></MdTableCell>
<MdTableCell head scope="row">Acme Corp</MdTableCell>
<MdTableCell numeric>$1,200.00</MdTableCell>
<MdTableCell>Paid</MdTableCell>
<div slot="expanded" style={{ padding: '12px 16px', color: 'var(--md-sys-color-on-surface-variant)' }}>Three line items · paid by card ending 4242 on 4 May.</div>
</MdTableRow>
<MdTableRow value="e2" expandable>
<MdTableCell padding="checkbox"><MdTableExpandToggle buttonLabel="Expand details for Globex"></MdTableExpandToggle></MdTableCell>
<MdTableCell head scope="row">Globex</MdTableCell>
<MdTableCell numeric>$3,120.00</MdTableCell>
<MdTableCell>Pending</MdTableCell>
<div slot="expanded" style={{ padding: '12px 16px', color: 'var(--md-sys-color-on-surface-variant)' }}>Awaiting PO approval — chased 2 days ago.</div>
</MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-table-container variant="outlined">
<md-table column-template="auto 2fr 1fr 1fr" label="Expandable invoices" keep-height="false">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox"></md-table-cell>
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="e1" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Acme Corp"></md-table-expand-toggle></md-table-cell>
<md-table-cell head scope="row">Acme Corp</md-table-cell>
<md-table-cell numeric>$1,200.00</md-table-cell>
<md-table-cell>Paid</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Three line items · paid by card ending 4242 on 4 May.</div>
</md-table-row>
<md-table-row value="e2" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Globex"></md-table-expand-toggle></md-table-cell>
<md-table-cell head scope="row">Globex</md-table-cell>
<md-table-cell numeric>$3,120.00</md-table-cell>
<md-table-cell>Pending</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Awaiting PO approval — chased 2 days ago.</div>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-table-container variant="outlined">
<md-table column-template="auto 2fr 1fr 1fr" label="Expandable invoices" keep-height="false">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox"></md-table-cell>
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="e1" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Acme Corp"></md-table-expand-toggle></md-table-cell>
<md-table-cell head scope="row">Acme Corp</md-table-cell>
<md-table-cell numeric>$1,200.00</md-table-cell>
<md-table-cell>Paid</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Three line items · paid by card ending 4242 on 4 May.</div>
</md-table-row>
<md-table-row value="e2" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Globex"></md-table-expand-toggle></md-table-cell>
<md-table-cell head scope="row">Globex</md-table-cell>
<md-table-cell numeric>$3,120.00</md-table-cell>
<md-table-cell>Pending</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Awaiting PO approval — chased 2 days ago.</div>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</template><script>
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined">
<md-table column-template="auto 2fr 1fr 1fr" label="Expandable invoices" keep-height="false">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox"></md-table-cell>
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="e1" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Acme Corp"></md-table-expand-toggle></md-table-cell>
<md-table-cell head scope="row">Acme Corp</md-table-cell>
<md-table-cell numeric>$1,200.00</md-table-cell>
<md-table-cell>Paid</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Three line items · paid by card ending 4242 on 4 May.</div>
</md-table-row>
<md-table-row value="e2" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Globex"></md-table-expand-toggle></md-table-cell>
<md-table-cell head scope="row">Globex</md-table-cell>
<md-table-cell numeric>$3,120.00</md-table-cell>
<md-table-cell>Pending</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Awaiting PO approval — chased 2 days ago.</div>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>Click a row (or focus one and press Enter).
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Clickable invoices">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="Acme Corp" clickable><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="Globex" clickable><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
<md-table-row value="Initech" clickable><md-table-cell head scope="row">Initech</md-table-cell><md-table-cell numeric>$860.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<p id="click-out" role="status">Click a row (or focus one and press Enter).</p>
<script type="module">
import '@awc-ui/core/define';
// `clickable` gives the row role="button", a pointer cursor, a ripple and
// Enter/Space activation — so one listener covers mouse and keyboard.
// mdRowClick bubbles, so listening on the table catches every row.
const out = document.getElementById('click-out');
document.querySelector('md-table').addEventListener('mdRowClick', (e) => {
out.textContent = `Opened invoice for ${e.detail.value}.`;
});
</script>import { useState } from 'react';
import { MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead, MdTableRow } from '@awc-ui/react';
const INVOICES = [
{ id: 'Acme Corp', client: 'Acme Corp', amount: 1200, status: 'Paid' },
{ id: 'Globex', client: 'Globex', amount: 3120, status: 'Pending' },
{ id: 'Initech', client: 'Initech', amount: 860, status: 'Paid' },
];
const money = (n) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
export function ClickableInvoices() {
const [message, setMessage] = useState('Click a row (or focus one and press Enter).');
return (
<>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="2fr 1fr 1fr" label="Clickable invoices">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
{INVOICES.map((row) => (
// `clickable` adds role="button", the pointer cursor, a ripple and
// Enter/Space activation — one handler serves mouse and keyboard.
<MdTableRow
key={row.id}
value={row.id}
clickable
onMdRowClick={(e) => setMessage(`Opened invoice for ${e.detail.value}.`)}
>
<MdTableCell head scope="row">{row.client}</MdTableCell>
<MdTableCell numeric>{money(row.amount)}</MdTableCell>
<MdTableCell>{row.status}</MdTableCell>
</MdTableRow>
))}
</MdTableBody>
</MdTable>
</MdTableContainer>
<p role="status">{message}</p>
</>
);
}// clickable-invoices.component.ts
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { CommonModule } from '@angular/common';
import { defineCustomElements } from '@awc-ui/core/loader';
defineCustomElements(window);
interface Invoice { id: string; client: string; amount: number; status: string; }
@Component({
selector: 'app-clickable-invoices',
standalone: true,
imports: [CommonModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `
<md-table-container variant="outlined">
<!-- mdRowClick bubbles, so one listener on the table covers every row. -->
<md-table column-template="2fr 1fr 1fr" label="Clickable invoices" (mdRowClick)="onRowClick($event)">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row *ngFor="let row of invoices; trackBy: trackById" [attr.value]="row.id" clickable>
<md-table-cell head scope="row">{{ row.client }}</md-table-cell>
<md-table-cell numeric>{{ row.amount | currency }}</md-table-cell>
<md-table-cell>{{ row.status }}</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<p role="status">{{ message }}</p>
`,
})
export class ClickableInvoicesComponent {
invoices: Invoice[] = [
{ id: 'Acme Corp', client: 'Acme Corp', amount: 1200, status: 'Paid' },
{ id: 'Globex', client: 'Globex', amount: 3120, status: 'Pending' },
{ id: 'Initech', client: 'Initech', amount: 860, status: 'Paid' },
];
message = 'Click a row (or focus one and press Enter).';
onRowClick(event: Event) {
const { value } = (event as CustomEvent<{ value: string }>).detail;
this.message = `Opened invoice for ${value}.`;
}
trackById = (_: number, row: Invoice) => row.id;
}<script setup>
import { ref } from 'vue';
import '@awc-ui/core/define';
const INVOICES = [
{ id: 'Acme Corp', client: 'Acme Corp', amount: 1200, status: 'Paid' },
{ id: 'Globex', client: 'Globex', amount: 3120, status: 'Pending' },
{ id: 'Initech', client: 'Initech', amount: 860, status: 'Paid' },
];
const money = (n) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
const message = ref('Click a row (or focus one and press Enter).');
const onRowClick = (e) => { message.value = `Opened invoice for ${e.detail.value}.`; };
</script>
<template>
<md-table-container variant="outlined">
<!-- mdRowClick bubbles: one listener on the table serves every row. -->
<md-table column-template="2fr 1fr 1fr" label="Clickable invoices" @mdRowClick="onRowClick">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row v-for="row in INVOICES" :key="row.id" :value="row.id" clickable>
<md-table-cell head scope="row">{{ row.client }}</md-table-cell>
<md-table-cell numeric>{{ money(row.amount) }}</md-table-cell>
<md-table-cell>{{ row.status }}</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<p role="status">{{ message }}</p>
</template><script>
import '@awc-ui/core/define';
const INVOICES = [
{ id: 'Acme Corp', client: 'Acme Corp', amount: 1200, status: 'Paid' },
{ id: 'Globex', client: 'Globex', amount: 3120, status: 'Pending' },
{ id: 'Initech', client: 'Initech', amount: 860, status: 'Paid' },
];
const money = (n) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
let message = 'Click a row (or focus one and press Enter).';
</script>
<md-table-container variant="outlined">
<!-- mdRowClick bubbles: one listener on the table serves every row. -->
<md-table
column-template="2fr 1fr 1fr"
label="Clickable invoices"
on:mdRowClick={(e) => (message = `Opened invoice for ${e.detail.value}.`)}
>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
{#each INVOICES as row (row.id)}
<md-table-row value={row.id} clickable>
<md-table-cell head scope="row">{row.client}</md-table-cell>
<md-table-cell numeric>{money(row.amount)}</md-table-cell>
<md-table-cell>{row.status}</md-table-cell>
</md-table-row>
{/each}
</md-table-body>
</md-table>
</md-table-container>
<p role="status">{message}</p><!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Editable cells">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="e1">
<md-table-cell head scope="row">Acme Corp</md-table-cell>
<md-table-cell numeric><md-text-field label="Amount" variant="outlined" value="1200.00" density="-2" style="inline-size: 130px;"></md-text-field></md-table-cell>
<md-table-cell><md-select label="Status" variant="outlined" density="-2" value="paid" style="inline-size: 130px;"><md-select-option value="paid">Paid</md-select-option><md-select-option value="pending">Pending</md-select-option></md-select></md-table-cell>
</md-table-row>
<md-table-row value="e2">
<md-table-cell head scope="row">Globex</md-table-cell>
<md-table-cell numeric><md-text-field label="Amount" variant="outlined" value="3120.00" density="-2" style="inline-size: 130px;"></md-text-field></md-table-cell>
<md-table-cell><md-select label="Status" variant="outlined" density="-2" value="pending" style="inline-size: 130px;"><md-select-option value="paid">Paid</md-select-option><md-select-option value="pending">Pending</md-select-option></md-select></md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>import { MdSelect, MdSelectOption, MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead, MdTableRow, MdTextField } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="2fr 1fr 1fr" label="Editable cells">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="e1">
<MdTableCell head scope="row">Acme Corp</MdTableCell>
<MdTableCell numeric><MdTextField label="Amount" variant="outlined" value="1200.00" density="-2" style={{ inlineSize: '130px' }}></MdTextField></MdTableCell>
<MdTableCell><MdSelect label="Status" variant="outlined" density="-2" value="paid" style={{ inlineSize: '130px' }}><MdSelectOption value="paid">Paid</MdSelectOption><MdSelectOption value="pending">Pending</MdSelectOption></MdSelect></MdTableCell>
</MdTableRow>
<MdTableRow value="e2">
<MdTableCell head scope="row">Globex</MdTableCell>
<MdTableCell numeric><MdTextField label="Amount" variant="outlined" value="3120.00" density="-2" style={{ inlineSize: '130px' }}></MdTextField></MdTableCell>
<MdTableCell><MdSelect label="Status" variant="outlined" density="-2" value="pending" style={{ inlineSize: '130px' }}><MdSelectOption value="paid">Paid</MdSelectOption><MdSelectOption value="pending">Pending</MdSelectOption></MdSelect></MdTableCell>
</MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Editable cells">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="e1">
<md-table-cell head scope="row">Acme Corp</md-table-cell>
<md-table-cell numeric><md-text-field label="Amount" variant="outlined" value="1200.00" density="-2" style="inline-size: 130px;"></md-text-field></md-table-cell>
<md-table-cell><md-select label="Status" variant="outlined" density="-2" value="paid" style="inline-size: 130px;"><md-select-option value="paid">Paid</md-select-option><md-select-option value="pending">Pending</md-select-option></md-select></md-table-cell>
</md-table-row>
<md-table-row value="e2">
<md-table-cell head scope="row">Globex</md-table-cell>
<md-table-cell numeric><md-text-field label="Amount" variant="outlined" value="3120.00" density="-2" style="inline-size: 130px;"></md-text-field></md-table-cell>
<md-table-cell><md-select label="Status" variant="outlined" density="-2" value="pending" style="inline-size: 130px;"><md-select-option value="paid">Paid</md-select-option><md-select-option value="pending">Pending</md-select-option></md-select></md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Editable cells">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="e1">
<md-table-cell head scope="row">Acme Corp</md-table-cell>
<md-table-cell numeric><md-text-field label="Amount" variant="outlined" value="1200.00" density="-2" style="inline-size: 130px;"></md-text-field></md-table-cell>
<md-table-cell><md-select label="Status" variant="outlined" density="-2" value="paid" style="inline-size: 130px;"><md-select-option value="paid">Paid</md-select-option><md-select-option value="pending">Pending</md-select-option></md-select></md-table-cell>
</md-table-row>
<md-table-row value="e2">
<md-table-cell head scope="row">Globex</md-table-cell>
<md-table-cell numeric><md-text-field label="Amount" variant="outlined" value="3120.00" density="-2" style="inline-size: 130px;"></md-text-field></md-table-cell>
<md-table-cell><md-select label="Status" variant="outlined" density="-2" value="pending" style="inline-size: 130px;"><md-select-option value="paid">Paid</md-select-option><md-select-option value="pending">Pending</md-select-option></md-select></md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</template><script>
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Editable cells">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="e1">
<md-table-cell head scope="row">Acme Corp</md-table-cell>
<md-table-cell numeric><md-text-field label="Amount" variant="outlined" value="1200.00" density="-2" style="inline-size: 130px;"></md-text-field></md-table-cell>
<md-table-cell><md-select label="Status" variant="outlined" density="-2" value="paid" style="inline-size: 130px;"><md-select-option value="paid">Paid</md-select-option><md-select-option value="pending">Pending</md-select-option></md-select></md-table-cell>
</md-table-row>
<md-table-row value="e2">
<md-table-cell head scope="row">Globex</md-table-cell>
<md-table-cell numeric><md-text-field label="Amount" variant="outlined" value="3120.00" density="-2" style="inline-size: 130px;"></md-text-field></md-table-cell>
<md-table-cell><md-select label="Status" variant="outlined" density="-2" value="pending" style="inline-size: 130px;"><md-select-option value="paid">Paid</md-select-option><md-select-option value="pending">Pending</md-select-option></md-select></md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>In Storybook: row selection, clickable rows, editable cells.
| Prop | Effect |
|---|---|
loading + loading-mode="overlay" (default) | Indeterminate progress line under the header plus a scrim that dims and disables the body; header and footer stay put |
loading + loading-mode="skeleton" | The body is replaced by loading-rows shimmering skeleton rows (default 4); header and footer untouched |
empty | Renders the empty slot instead of rows |
<md-table loading loading-mode="skeleton" loading-rows="6"></md-table>
<md-table empty> <div slot="empty">No invoices yet.</div></md-table>keep-height (default on) is a height ratchet: once rendered, the table
never shrinks below that initial height, so paging, filtering to zero rows or an
async refetch can’t make the page jump or flash a scrollbar. Set
keep-height="false" for tables that are meant to resize — a density-toggle
demo, for instance.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div style="display: grid; gap: 20px;">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Loading, skeleton mode" loading loading-mode="skeleton" loading-rows="3">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body></md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="No results" empty>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body></md-table-body>
<div slot="empty" style="padding: 24px; text-align: center; color: var(--md-sys-color-on-surface-variant);">No invoices match this filter.</div>
</md-table>
</md-table-container>
</div>import { MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead, MdTableRow } from '@awc-ui/react';
export function Demo() {
return (
<>
<div style={{ display: 'grid', gap: '20px' }}>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="2fr 1fr 1fr" label="Loading, skeleton mode" loading loadingMode="skeleton" loadingRows="3">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody></MdTableBody>
</MdTable>
</MdTableContainer>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="2fr 1fr 1fr" label="No results" empty>
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody></MdTableBody>
<div slot="empty" style={{ padding: '24px', textAlign: 'center', color: 'var(--md-sys-color-on-surface-variant)' }}>No invoices match this filter.</div>
</MdTable>
</MdTableContainer>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div style="display: grid; gap: 20px;">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Loading, skeleton mode" loading loading-mode="skeleton" loading-rows="3">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body></md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="No results" empty>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body></md-table-body>
<div slot="empty" style="padding: 24px; text-align: center; color: var(--md-sys-color-on-surface-variant);">No invoices match this filter.</div>
</md-table>
</md-table-container>
</div><script setup>
import '@awc-ui/core/define';
</script>
<template>
<div style="display: grid; gap: 20px;">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Loading, skeleton mode" loading loading-mode="skeleton" loading-rows="3">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body></md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="No results" empty>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body></md-table-body>
<div slot="empty" style="padding: 24px; text-align: center; color: var(--md-sys-color-on-surface-variant);">No invoices match this filter.</div>
</md-table>
</md-table-container>
</div>
</template><script>
import '@awc-ui/core/define';
</script>
<div style="display: grid; gap: 20px;">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Loading, skeleton mode" loading loading-mode="skeleton" loading-rows="3">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body></md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="No results" empty>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body></md-table-body>
<div slot="empty" style="padding: 24px; text-align: center; color: var(--md-sys-color-on-surface-variant);">No invoices match this filter.</div>
</md-table>
</md-table-container>
</div>loading-mode="overlay" is the other half of the pair. Use it once rows are on
screen — the existing data stays readable under an indeterminate line instead of
being replaced by a skeleton, so a refresh or a page change doesn’t blank the
table:
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div style="display: grid; gap: 20px;">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Loading, overlay mode" loading loading-mode="overlay">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Loading, overlay mode over striped rows" loading loading-mode="overlay" striped>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>import { MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead, MdTableRow } from '@awc-ui/react';
export function Demo() {
return (
<>
<div style={{ display: 'grid', gap: '20px' }}>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="2fr 1fr 1fr" label="Loading, overlay mode" loading loadingMode="overlay">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="r1"><MdTableCell head scope="row">Acme Corp</MdTableCell><MdTableCell numeric>$1,200.00</MdTableCell><MdTableCell>Paid</MdTableCell></MdTableRow>
<MdTableRow value="r2"><MdTableCell head scope="row">Globex</MdTableCell><MdTableCell numeric>$3,120.00</MdTableCell><MdTableCell>Pending</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="2fr 1fr 1fr" label="Loading, overlay mode over striped rows" loading loadingMode="overlay" striped>
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="r1"><MdTableCell head scope="row">Acme Corp</MdTableCell><MdTableCell numeric>$1,200.00</MdTableCell><MdTableCell>Paid</MdTableCell></MdTableRow>
<MdTableRow value="r2"><MdTableCell head scope="row">Globex</MdTableCell><MdTableCell numeric>$3,120.00</MdTableCell><MdTableCell>Pending</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div style="display: grid; gap: 20px;">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Loading, overlay mode" loading loading-mode="overlay">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Loading, overlay mode over striped rows" loading loading-mode="overlay" striped>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div><script setup>
import '@awc-ui/core/define';
</script>
<template>
<div style="display: grid; gap: 20px;">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Loading, overlay mode" loading loading-mode="overlay">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Loading, overlay mode over striped rows" loading loading-mode="overlay" striped>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
</template><script>
import '@awc-ui/core/define';
</script>
<div style="display: grid; gap: 20px;">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Loading, overlay mode" loading loading-mode="overlay">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Loading, overlay mode over striped rows" loading loading-mode="overlay" striped>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>With no empty slot the table falls back to its own “No data” message. Slot
your own when you can say something more useful — why it is empty, and what to
do next:
<!-- index.html <head> — the icon font the components draw from -->
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&display=swap">
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div style="display: grid; gap: 20px;">
<md-table-container variant="outlined">
<md-table column-template="1.5fr 1fr 1fr" label="Default empty message" empty>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col">Role</md-table-cell>
<md-table-cell head scope="col" numeric>Salary</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body></md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="1.5fr 1fr 1fr" label="No results for the filter" empty>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col">Role</md-table-cell>
<md-table-cell head scope="col" numeric>Salary</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body></md-table-body>
<div slot="empty" style="display: grid; justify-items: center; gap: 12px; padding: 40px 24px; text-align: center;">
<span class="material-symbols-outlined" style="font-size: 40px; color: var(--md-sys-color-on-surface-variant);">search_off</span>
<div style="display: grid; gap: 4px;">
<div style="font: var(--md-sys-typescale-title-medium); color: var(--md-sys-color-on-surface);">No employees match your filters</div>
<div style="font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">Adjust your filters, or reset them.</div>
</div>
<md-button variant="filled">Reset filters</md-button>
</div>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="1.5fr 1fr 1fr" label="First-run empty" empty>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col">Role</md-table-cell>
<md-table-cell head scope="col" numeric>Salary</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body></md-table-body>
<div slot="empty" style="display: grid; justify-items: center; gap: 12px; padding: 40px 24px; text-align: center;">
<span class="material-symbols-outlined" style="font-size: 40px; color: var(--md-sys-color-on-surface-variant);">group_add</span>
<div style="display: grid; gap: 4px;">
<div style="font: var(--md-sys-typescale-title-medium); color: var(--md-sys-color-on-surface);">No employees yet</div>
<div style="font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">Add your first team member to get started.</div>
</div>
<md-button variant="filled">Add employee</md-button>
</div>
</md-table>
</md-table-container>
</div>// Icons need the Material Symbols stylesheet in index.html — see Installation.
import { MdButton, MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead, MdTableRow } from '@awc-ui/react';
export function Demo() {
return (
<>
<div style={{ display: 'grid', gap: '20px' }}>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="1.5fr 1fr 1fr" label="Default empty message" empty>
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Name</MdTableCell>
<MdTableCell head scope="col">Role</MdTableCell>
<MdTableCell head scope="col" numeric>Salary</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody></MdTableBody>
</MdTable>
</MdTableContainer>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="1.5fr 1fr 1fr" label="No results for the filter" empty>
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Name</MdTableCell>
<MdTableCell head scope="col">Role</MdTableCell>
<MdTableCell head scope="col" numeric>Salary</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody></MdTableBody>
<div slot="empty" style={{ display: 'grid', justifyItems: 'center', gap: '12px', padding: '40px 24px', textAlign: 'center' }}>
<span className="material-symbols-outlined" style={{ fontSize: '40px', color: 'var(--md-sys-color-on-surface-variant)' }}>search_off</span>
<div style={{ display: 'grid', gap: '4px' }}>
<div style={{ font: 'var(--md-sys-typescale-title-medium)', color: 'var(--md-sys-color-on-surface)' }}>No employees match your filters</div>
<div style={{ font: 'var(--md-sys-typescale-body-medium)', color: 'var(--md-sys-color-on-surface-variant)' }}>Adjust your filters, or reset them.</div>
</div>
<MdButton variant="filled">Reset filters</MdButton>
</div>
</MdTable>
</MdTableContainer>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="1.5fr 1fr 1fr" label="First-run empty" empty>
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Name</MdTableCell>
<MdTableCell head scope="col">Role</MdTableCell>
<MdTableCell head scope="col" numeric>Salary</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody></MdTableBody>
<div slot="empty" style={{ display: 'grid', justifyItems: 'center', gap: '12px', padding: '40px 24px', textAlign: 'center' }}>
<span className="material-symbols-outlined" style={{ fontSize: '40px', color: 'var(--md-sys-color-on-surface-variant)' }}>group_add</span>
<div style={{ display: 'grid', gap: '4px' }}>
<div style={{ font: 'var(--md-sys-typescale-title-medium)', color: 'var(--md-sys-color-on-surface)' }}>No employees yet</div>
<div style={{ font: 'var(--md-sys-typescale-body-medium)', color: 'var(--md-sys-color-on-surface-variant)' }}>Add your first team member to get started.</div>
</div>
<MdButton variant="filled">Add employee</MdButton>
</div>
</MdTable>
</MdTableContainer>
</div>
</>
);
}// Icons need the Material Symbols stylesheet in index.html — see Installation.
// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div style="display: grid; gap: 20px;">
<md-table-container variant="outlined">
<md-table column-template="1.5fr 1fr 1fr" label="Default empty message" empty>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col">Role</md-table-cell>
<md-table-cell head scope="col" numeric>Salary</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body></md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="1.5fr 1fr 1fr" label="No results for the filter" empty>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col">Role</md-table-cell>
<md-table-cell head scope="col" numeric>Salary</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body></md-table-body>
<div slot="empty" style="display: grid; justify-items: center; gap: 12px; padding: 40px 24px; text-align: center;">
<span class="material-symbols-outlined" style="font-size: 40px; color: var(--md-sys-color-on-surface-variant);">search_off</span>
<div style="display: grid; gap: 4px;">
<div style="font: var(--md-sys-typescale-title-medium); color: var(--md-sys-color-on-surface);">No employees match your filters</div>
<div style="font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">Adjust your filters, or reset them.</div>
</div>
<md-button variant="filled">Reset filters</md-button>
</div>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="1.5fr 1fr 1fr" label="First-run empty" empty>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col">Role</md-table-cell>
<md-table-cell head scope="col" numeric>Salary</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body></md-table-body>
<div slot="empty" style="display: grid; justify-items: center; gap: 12px; padding: 40px 24px; text-align: center;">
<span class="material-symbols-outlined" style="font-size: 40px; color: var(--md-sys-color-on-surface-variant);">group_add</span>
<div style="display: grid; gap: 4px;">
<div style="font: var(--md-sys-typescale-title-medium); color: var(--md-sys-color-on-surface);">No employees yet</div>
<div style="font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">Add your first team member to get started.</div>
</div>
<md-button variant="filled">Add employee</md-button>
</div>
</md-table>
</md-table-container>
</div><script setup>
// Icons need the Material Symbols stylesheet in index.html — see Installation.
import '@awc-ui/core/define';
</script>
<template>
<div style="display: grid; gap: 20px;">
<md-table-container variant="outlined">
<md-table column-template="1.5fr 1fr 1fr" label="Default empty message" empty>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col">Role</md-table-cell>
<md-table-cell head scope="col" numeric>Salary</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body></md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="1.5fr 1fr 1fr" label="No results for the filter" empty>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col">Role</md-table-cell>
<md-table-cell head scope="col" numeric>Salary</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body></md-table-body>
<div slot="empty" style="display: grid; justify-items: center; gap: 12px; padding: 40px 24px; text-align: center;">
<span class="material-symbols-outlined" style="font-size: 40px; color: var(--md-sys-color-on-surface-variant);">search_off</span>
<div style="display: grid; gap: 4px;">
<div style="font: var(--md-sys-typescale-title-medium); color: var(--md-sys-color-on-surface);">No employees match your filters</div>
<div style="font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">Adjust your filters, or reset them.</div>
</div>
<md-button variant="filled">Reset filters</md-button>
</div>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="1.5fr 1fr 1fr" label="First-run empty" empty>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col">Role</md-table-cell>
<md-table-cell head scope="col" numeric>Salary</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body></md-table-body>
<div slot="empty" style="display: grid; justify-items: center; gap: 12px; padding: 40px 24px; text-align: center;">
<span class="material-symbols-outlined" style="font-size: 40px; color: var(--md-sys-color-on-surface-variant);">group_add</span>
<div style="display: grid; gap: 4px;">
<div style="font: var(--md-sys-typescale-title-medium); color: var(--md-sys-color-on-surface);">No employees yet</div>
<div style="font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">Add your first team member to get started.</div>
</div>
<md-button variant="filled">Add employee</md-button>
</div>
</md-table>
</md-table-container>
</div>
</template><script>
// Icons need the Material Symbols stylesheet in index.html — see Installation.
import '@awc-ui/core/define';
</script>
<div style="display: grid; gap: 20px;">
<md-table-container variant="outlined">
<md-table column-template="1.5fr 1fr 1fr" label="Default empty message" empty>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col">Role</md-table-cell>
<md-table-cell head scope="col" numeric>Salary</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body></md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="1.5fr 1fr 1fr" label="No results for the filter" empty>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col">Role</md-table-cell>
<md-table-cell head scope="col" numeric>Salary</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body></md-table-body>
<div slot="empty" style="display: grid; justify-items: center; gap: 12px; padding: 40px 24px; text-align: center;">
<span class="material-symbols-outlined" style="font-size: 40px; color: var(--md-sys-color-on-surface-variant);">search_off</span>
<div style="display: grid; gap: 4px;">
<div style="font: var(--md-sys-typescale-title-medium); color: var(--md-sys-color-on-surface);">No employees match your filters</div>
<div style="font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">Adjust your filters, or reset them.</div>
</div>
<md-button variant="filled">Reset filters</md-button>
</div>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="1.5fr 1fr 1fr" label="First-run empty" empty>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Name</md-table-cell>
<md-table-cell head scope="col">Role</md-table-cell>
<md-table-cell head scope="col" numeric>Salary</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body></md-table-body>
<div slot="empty" style="display: grid; justify-items: center; gap: 12px; padding: 40px 24px; text-align: center;">
<span class="material-symbols-outlined" style="font-size: 40px; color: var(--md-sys-color-on-surface-variant);">group_add</span>
<div style="display: grid; gap: 4px;">
<div style="font: var(--md-sys-typescale-title-medium); color: var(--md-sys-color-on-surface);">No employees yet</div>
<div style="font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">Add your first team member to get started.</div>
</div>
<md-button variant="filled">Add employee</md-button>
</div>
</md-table>
</md-table-container>
</div>The empty state is its own flag — empty swaps the body for the empty slot
without touching the header, so column widths stay put while a filter is
cleared:
Each state has its own story: loading and empty.
Slot your own into loader when the default linear bar isn’t the right
affordance — a circular md-progress-indicator,
a branded mark, a skeleton.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr" label="Slotted loader" loading loading-mode="overlay">
<md-progress-indicator slot="loader" variant="circular" indeterminate size="32" label="Loading invoices"></md-progress-indicator>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row>
<md-table-cell>Acme</md-table-cell>
<md-table-cell numeric>1,200</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>import { MdProgressIndicator, MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead, MdTableRow } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="2fr 1fr" label="Slotted loader" loading loadingMode="overlay">
<MdProgressIndicator slot="loader" variant="circular" indeterminate size="32" label="Loading invoices"></MdProgressIndicator>
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow>
<MdTableCell>Acme</MdTableCell>
<MdTableCell numeric>1,200</MdTableCell>
</MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr" label="Slotted loader" loading loading-mode="overlay">
<md-progress-indicator slot="loader" variant="circular" indeterminate size="32" label="Loading invoices"></md-progress-indicator>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row>
<md-table-cell>Acme</md-table-cell>
<md-table-cell numeric>1,200</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr" label="Slotted loader" loading loading-mode="overlay">
<md-progress-indicator slot="loader" variant="circular" indeterminate size="32" label="Loading invoices"></md-progress-indicator>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row>
<md-table-cell>Acme</md-table-cell>
<md-table-cell numeric>1,200</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</template><script>
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr" label="Slotted loader" loading loading-mode="overlay">
<md-progress-indicator slot="loader" variant="circular" indeterminate size="32" label="Loading invoices"></md-progress-indicator>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row>
<md-table-cell>Acme</md-table-cell>
<md-table-cell numeric>1,200</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>The two are not exclusive: the semantic sizes are themselves scale-aware, so
density="compact" inside a data-density="-2" region condenses further. A
numeric value drives --md-sys-density-scale on the host exactly as a global
data-density ancestor would, and takes the standard row-height base.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div style="display: grid; gap: 20px;">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Comfortable" density="comfortable">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Standard" density="standard">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Compact" density="compact">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>import { MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead, MdTableRow } from '@awc-ui/react';
export function Demo() {
return (
<>
<div style={{ display: 'grid', gap: '20px' }}>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="2fr 1fr 1fr" label="Comfortable" density="comfortable">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="r1"><MdTableCell head scope="row">Acme Corp</MdTableCell><MdTableCell numeric>$1,200.00</MdTableCell><MdTableCell>Paid</MdTableCell></MdTableRow>
<MdTableRow value="r2"><MdTableCell head scope="row">Globex</MdTableCell><MdTableCell numeric>$3,120.00</MdTableCell><MdTableCell>Pending</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="2fr 1fr 1fr" label="Standard" density="standard">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="r1"><MdTableCell head scope="row">Acme Corp</MdTableCell><MdTableCell numeric>$1,200.00</MdTableCell><MdTableCell>Paid</MdTableCell></MdTableRow>
<MdTableRow value="r2"><MdTableCell head scope="row">Globex</MdTableCell><MdTableCell numeric>$3,120.00</MdTableCell><MdTableCell>Pending</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="2fr 1fr 1fr" label="Compact" density="compact">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="r1"><MdTableCell head scope="row">Acme Corp</MdTableCell><MdTableCell numeric>$1,200.00</MdTableCell><MdTableCell>Paid</MdTableCell></MdTableRow>
<MdTableRow value="r2"><MdTableCell head scope="row">Globex</MdTableCell><MdTableCell numeric>$3,120.00</MdTableCell><MdTableCell>Pending</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div style="display: grid; gap: 20px;">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Comfortable" density="comfortable">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Standard" density="standard">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Compact" density="compact">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div><script setup>
import '@awc-ui/core/define';
</script>
<template>
<div style="display: grid; gap: 20px;">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Comfortable" density="comfortable">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Standard" density="standard">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Compact" density="compact">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
</template><script>
import '@awc-ui/core/define';
</script>
<div style="display: grid; gap: 20px;">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Comfortable" density="comfortable">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Standard" density="standard">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Compact" density="compact">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>The numeric rungs are the other vocabulary — the same -4 … 0 scale every
other component takes, and the same signal a global data-density ancestor
sets. Row height, cell padding and type all step down together:
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div style="display: grid; gap: 20px;">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Rung 0" density="0">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Rung -1" density="-1">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Rung -2" density="-2">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Rung -3" density="-3">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Rung -4" density="-4">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>import { MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead, MdTableRow } from '@awc-ui/react';
export function Demo() {
return (
<>
<div style={{ display: 'grid', gap: '20px' }}>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="2fr 1fr 1fr" label="Rung 0" density="0">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="r1"><MdTableCell head scope="row">Acme Corp</MdTableCell><MdTableCell numeric>$1,200.00</MdTableCell><MdTableCell>Paid</MdTableCell></MdTableRow>
<MdTableRow value="r2"><MdTableCell head scope="row">Globex</MdTableCell><MdTableCell numeric>$3,120.00</MdTableCell><MdTableCell>Pending</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="2fr 1fr 1fr" label="Rung -1" density="-1">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="r1"><MdTableCell head scope="row">Acme Corp</MdTableCell><MdTableCell numeric>$1,200.00</MdTableCell><MdTableCell>Paid</MdTableCell></MdTableRow>
<MdTableRow value="r2"><MdTableCell head scope="row">Globex</MdTableCell><MdTableCell numeric>$3,120.00</MdTableCell><MdTableCell>Pending</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="2fr 1fr 1fr" label="Rung -2" density="-2">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="r1"><MdTableCell head scope="row">Acme Corp</MdTableCell><MdTableCell numeric>$1,200.00</MdTableCell><MdTableCell>Paid</MdTableCell></MdTableRow>
<MdTableRow value="r2"><MdTableCell head scope="row">Globex</MdTableCell><MdTableCell numeric>$3,120.00</MdTableCell><MdTableCell>Pending</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="2fr 1fr 1fr" label="Rung -3" density="-3">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="r1"><MdTableCell head scope="row">Acme Corp</MdTableCell><MdTableCell numeric>$1,200.00</MdTableCell><MdTableCell>Paid</MdTableCell></MdTableRow>
<MdTableRow value="r2"><MdTableCell head scope="row">Globex</MdTableCell><MdTableCell numeric>$3,120.00</MdTableCell><MdTableCell>Pending</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="2fr 1fr 1fr" label="Rung -4" density="-4">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="r1"><MdTableCell head scope="row">Acme Corp</MdTableCell><MdTableCell numeric>$1,200.00</MdTableCell><MdTableCell>Paid</MdTableCell></MdTableRow>
<MdTableRow value="r2"><MdTableCell head scope="row">Globex</MdTableCell><MdTableCell numeric>$3,120.00</MdTableCell><MdTableCell>Pending</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div style="display: grid; gap: 20px;">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Rung 0" density="0">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Rung -1" density="-1">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Rung -2" density="-2">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Rung -3" density="-3">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Rung -4" density="-4">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div><script setup>
import '@awc-ui/core/define';
</script>
<template>
<div style="display: grid; gap: 20px;">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Rung 0" density="0">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Rung -1" density="-1">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Rung -2" density="-2">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Rung -3" density="-3">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Rung -4" density="-4">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
</template><script>
import '@awc-ui/core/define';
</script>
<div style="display: grid; gap: 20px;">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Rung 0" density="0">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Rung -1" density="-1">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Rung -2" density="-2">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Rung -3" density="-3">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Rung -4" density="-4">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>The named presets are compact and comfortable; expressive motion shows the change animating.
md-table-foot holds aggregate rows, a cell can span tracks with colspan, and
a two-tier header groups columns above their leaves.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Invoices with totals">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="inv-1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="inv-2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
<md-table-foot>
<md-table-row rowgroup="foot" selectable="false">
<md-table-cell head scope="row">Total</md-table-cell>
<md-table-cell numeric>$4,320.00</md-table-cell>
<md-table-cell></md-table-cell>
</md-table-row>
</md-table-foot>
</md-table>
</md-table-container>import { MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableFoot, MdTableHead, MdTableRow } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="2fr 1fr 1fr" label="Invoices with totals">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="inv-1"><MdTableCell head scope="row">Acme Corp</MdTableCell><MdTableCell numeric>$1,200.00</MdTableCell><MdTableCell>Paid</MdTableCell></MdTableRow>
<MdTableRow value="inv-2"><MdTableCell head scope="row">Globex</MdTableCell><MdTableCell numeric>$3,120.00</MdTableCell><MdTableCell>Pending</MdTableCell></MdTableRow>
</MdTableBody>
<MdTableFoot>
<MdTableRow rowgroup="foot" selectable="false">
<MdTableCell head scope="row">Total</MdTableCell>
<MdTableCell numeric>$4,320.00</MdTableCell>
<MdTableCell></MdTableCell>
</MdTableRow>
</MdTableFoot>
</MdTable>
</MdTableContainer>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Invoices with totals">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="inv-1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="inv-2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
<md-table-foot>
<md-table-row rowgroup="foot" selectable="false">
<md-table-cell head scope="row">Total</md-table-cell>
<md-table-cell numeric>$4,320.00</md-table-cell>
<md-table-cell></md-table-cell>
</md-table-row>
</md-table-foot>
</md-table>
</md-table-container><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Invoices with totals">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="inv-1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="inv-2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
<md-table-foot>
<md-table-row rowgroup="foot" selectable="false">
<md-table-cell head scope="row">Total</md-table-cell>
<md-table-cell numeric>$4,320.00</md-table-cell>
<md-table-cell></md-table-cell>
</md-table-row>
</md-table-foot>
</md-table>
</md-table-container>
</template><script>
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Invoices with totals">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="inv-1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="inv-2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
<md-table-foot>
<md-table-row rowgroup="foot" selectable="false">
<md-table-cell head scope="row">Total</md-table-cell>
<md-table-cell numeric>$4,320.00</md-table-cell>
<md-table-cell></md-table-cell>
</md-table-row>
</md-table-foot>
</md-table>
</md-table-container><!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr 1fr" label="Grouped columns">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col"></md-table-cell>
<md-table-cell head scope="col" colspan="2" style="text-align: center;">2025</md-table-cell>
<md-table-cell head scope="col">Change</md-table-cell>
</md-table-row>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Region</md-table-cell>
<md-table-cell head scope="col" numeric>H1</md-table-cell>
<md-table-cell head scope="col" numeric>H2</md-table-cell>
<md-table-cell head scope="col" numeric>%</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="emea"><md-table-cell head scope="row">EMEA</md-table-cell><md-table-cell numeric>412</md-table-cell><md-table-cell numeric>498</md-table-cell><md-table-cell numeric>+21%</md-table-cell></md-table-row>
<md-table-row value="amer"><md-table-cell head scope="row">AMER</md-table-cell><md-table-cell numeric>388</md-table-cell><md-table-cell numeric>402</md-table-cell><md-table-cell numeric>+4%</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>import { MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead, MdTableRow } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="2fr 1fr 1fr 1fr" label="Grouped columns">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col"></MdTableCell>
<MdTableCell head scope="col" colSpan="2" style={{ textAlign: 'center' }}>2025</MdTableCell>
<MdTableCell head scope="col">Change</MdTableCell>
</MdTableRow>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Region</MdTableCell>
<MdTableCell head scope="col" numeric>H1</MdTableCell>
<MdTableCell head scope="col" numeric>H2</MdTableCell>
<MdTableCell head scope="col" numeric>%</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="emea"><MdTableCell head scope="row">EMEA</MdTableCell><MdTableCell numeric>412</MdTableCell><MdTableCell numeric>498</MdTableCell><MdTableCell numeric>+21%</MdTableCell></MdTableRow>
<MdTableRow value="amer"><MdTableCell head scope="row">AMER</MdTableCell><MdTableCell numeric>388</MdTableCell><MdTableCell numeric>402</MdTableCell><MdTableCell numeric>+4%</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr 1fr" label="Grouped columns">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col"></md-table-cell>
<md-table-cell head scope="col" colspan="2" style="text-align: center;">2025</md-table-cell>
<md-table-cell head scope="col">Change</md-table-cell>
</md-table-row>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Region</md-table-cell>
<md-table-cell head scope="col" numeric>H1</md-table-cell>
<md-table-cell head scope="col" numeric>H2</md-table-cell>
<md-table-cell head scope="col" numeric>%</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="emea"><md-table-cell head scope="row">EMEA</md-table-cell><md-table-cell numeric>412</md-table-cell><md-table-cell numeric>498</md-table-cell><md-table-cell numeric>+21%</md-table-cell></md-table-row>
<md-table-row value="amer"><md-table-cell head scope="row">AMER</md-table-cell><md-table-cell numeric>388</md-table-cell><md-table-cell numeric>402</md-table-cell><md-table-cell numeric>+4%</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr 1fr" label="Grouped columns">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col"></md-table-cell>
<md-table-cell head scope="col" colspan="2" style="text-align: center;">2025</md-table-cell>
<md-table-cell head scope="col">Change</md-table-cell>
</md-table-row>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Region</md-table-cell>
<md-table-cell head scope="col" numeric>H1</md-table-cell>
<md-table-cell head scope="col" numeric>H2</md-table-cell>
<md-table-cell head scope="col" numeric>%</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="emea"><md-table-cell head scope="row">EMEA</md-table-cell><md-table-cell numeric>412</md-table-cell><md-table-cell numeric>498</md-table-cell><md-table-cell numeric>+21%</md-table-cell></md-table-row>
<md-table-row value="amer"><md-table-cell head scope="row">AMER</md-table-cell><md-table-cell numeric>388</md-table-cell><md-table-cell numeric>402</md-table-cell><md-table-cell numeric>+4%</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</template><script>
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr 1fr" label="Grouped columns">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col"></md-table-cell>
<md-table-cell head scope="col" colspan="2" style="text-align: center;">2025</md-table-cell>
<md-table-cell head scope="col">Change</md-table-cell>
</md-table-row>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Region</md-table-cell>
<md-table-cell head scope="col" numeric>H1</md-table-cell>
<md-table-cell head scope="col" numeric>H2</md-table-cell>
<md-table-cell head scope="col" numeric>%</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="emea"><md-table-cell head scope="row">EMEA</md-table-cell><md-table-cell numeric>412</md-table-cell><md-table-cell numeric>498</md-table-cell><md-table-cell numeric>+21%</md-table-cell></md-table-row>
<md-table-row value="amer"><md-table-cell head scope="row">AMER</md-table-cell><md-table-cell numeric>388</md-table-cell><md-table-cell numeric>402</md-table-cell><md-table-cell numeric>+4%</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container><!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Spanning body cells">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
<md-table-row value="note" selectable="false">
<md-table-cell colspan="3" style="color: var(--md-sys-color-on-surface-variant); font-style: italic;">Remaining 39 invoices are archived.</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>import { MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead, MdTableRow } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="2fr 1fr 1fr" label="Spanning body cells">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="r1"><MdTableCell head scope="row">Acme Corp</MdTableCell><MdTableCell numeric>$1,200.00</MdTableCell><MdTableCell>Paid</MdTableCell></MdTableRow>
<MdTableRow value="r2"><MdTableCell head scope="row">Globex</MdTableCell><MdTableCell numeric>$3,120.00</MdTableCell><MdTableCell>Pending</MdTableCell></MdTableRow>
<MdTableRow value="note" selectable="false">
<MdTableCell colSpan="3" style={{ color: 'var(--md-sys-color-on-surface-variant)', fontStyle: 'italic' }}>Remaining 39 invoices are archived.</MdTableCell>
</MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Spanning body cells">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
<md-table-row value="note" selectable="false">
<md-table-cell colspan="3" style="color: var(--md-sys-color-on-surface-variant); font-style: italic;">Remaining 39 invoices are archived.</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Spanning body cells">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
<md-table-row value="note" selectable="false">
<md-table-cell colspan="3" style="color: var(--md-sys-color-on-surface-variant); font-style: italic;">Remaining 39 invoices are archived.</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</template><script>
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Spanning body cells">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
<md-table-row value="note" selectable="false">
<md-table-cell colspan="3" style="color: var(--md-sys-color-on-surface-variant); font-style: italic;">Remaining 39 invoices are archived.</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>The spanning story covers colspan in body cells, and
expandable rows puts detail content in a row’s expanded
slot.
md-table-pagination is a controlled bar: it reports intent through
mdPageChange / mdRowsPerPageChange and never slices your data. Feed it
count, page and rows-per-page and do the paging yourself — which is what
makes server-side pagination the same wiring as the
client-side story.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined" max-height="318px">
<md-table id="pag-table" frozen-header row-count="10" column-template="2fr 1fr 1fr" label="Paged invoices">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body id="pag-body">
<md-table-row value="p1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="p2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
<md-table-row value="p3"><md-table-cell head scope="row">Initech</md-table-cell><md-table-cell numeric>$860.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="p4"><md-table-cell head scope="row">Umbrella</md-table-cell><md-table-cell numeric>$2,450.00</md-table-cell><md-table-cell>Draft</md-table-cell></md-table-row>
<md-table-row value="p5"><md-table-cell head scope="row">Soylent</md-table-cell><md-table-cell numeric>$1,975.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="p6"><md-table-cell head scope="row">Hooli</md-table-cell><md-table-cell numeric>$4,300.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
<md-table-row value="p7"><md-table-cell head scope="row">Stark Industries</md-table-cell><md-table-cell numeric>$8,120.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="p8"><md-table-cell head scope="row">Wayne Enterprises</md-table-cell><md-table-cell numeric>$6,640.00</md-table-cell><md-table-cell>Draft</md-table-cell></md-table-row>
<md-table-row value="p9"><md-table-cell head scope="row">Cyberdyne</md-table-cell><md-table-cell numeric>$2,010.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="p10"><md-table-cell head scope="row">Tyrell Corp</md-table-cell><md-table-cell numeric>$3,780.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
<md-table-pagination id="pag" slot="bottom" count="10" page="0" rows-per-page="5"></md-table-pagination>
</md-table-container>
<script type="module">
var table = document.getElementById('pag-table');
var pag = document.getElementById('pag');
var page = 0;
var perPage = 5;
// Page by toggling row visibility rather than rebuilding rows — selection and
// expanded state then survive a page change for free.
var renderPage = function () {
var rows = Array.prototype.slice.call(document.querySelectorAll('#pag-body md-table-row'));
var start = page * perPage;
rows.forEach(function (r, i) { r.style.display = i >= start && i < start + perPage ? '' : 'none'; });
// NOTE: no row-offset here. It offsets aria-rowindex for rows that are NOT
// in the DOM; this demo keeps all ten rows in the DOM and only hides them,
// so the table already counts them and stamps the true absolute index.
// Adding row-offset would double-count (page 2 would announce rows 12-16
// against an aria-rowcount of 11). Use row-offset only when you render just
// the current page — see the server-pagination example below.
};
pag.addEventListener('mdPageChange', function (e) { page = e.detail.page; renderPage(); });
pag.addEventListener('mdRowsPerPageChange', function (e) { perPage = e.detail.rowsPerPage; page = 0; renderPage(); });
renderPage();
</script>import { useMemo, useState } from 'react';
import {
MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead,
MdTablePagination, MdTableRow,
} from '@awc-ui/react';
const INVOICES = [
{ id: 'p1', client: 'Acme Corp', amount: 1200, status: 'Paid' },
{ id: 'p2', client: 'Globex', amount: 3120, status: 'Pending' },
{ id: 'p3', client: 'Initech', amount: 860, status: 'Paid' },
{ id: 'p4', client: 'Umbrella', amount: 2450, status: 'Draft' },
{ id: 'p5', client: 'Soylent', amount: 1975, status: 'Paid' },
{ id: 'p6', client: 'Hooli', amount: 4300, status: 'Pending' },
{ id: 'p7', client: 'Stark Industries', amount: 8120, status: 'Paid' },
{ id: 'p8', client: 'Wayne Enterprises', amount: 6640, status: 'Draft' },
{ id: 'p9', client: 'Cyberdyne', amount: 2010, status: 'Paid' },
{ id: 'p10', client: 'Tyrell Corp', amount: 3780, status: 'Pending' },
];
const money = (n) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
export function PagedInvoices() {
const [page, setPage] = useState(0);
const [perPage, setPerPage] = useState(5);
const pageRows = useMemo(
() => INVOICES.slice(page * perPage, page * perPage + perPage),
[page, perPage],
);
return (
<MdTableContainer variant="outlined" maxHeight="318px">
{/* rowCount + rowOffset let the table stamp aria-rowindex against the
full set, so AT announces "row 7 of 10" rather than "row 2 of 5". */}
<MdTable
frozenHeader
rowCount={INVOICES.length}
rowOffset={page * perPage}
columnTemplate="2fr 1fr 1fr"
label="Paged invoices"
>
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
{pageRows.map((row) => (
<MdTableRow key={row.id} value={row.id}>
<MdTableCell head scope="row">{row.client}</MdTableCell>
<MdTableCell numeric>{money(row.amount)}</MdTableCell>
<MdTableCell>{row.status}</MdTableCell>
</MdTableRow>
))}
</MdTableBody>
</MdTable>
<MdTablePagination
slot="bottom"
count={INVOICES.length}
page={page}
rowsPerPage={perPage}
onMdPageChange={(e) => setPage(e.detail.page)}
onMdRowsPerPageChange={(e) => { setPerPage(e.detail.rowsPerPage); setPage(0); }}
/>
</MdTableContainer>
);
}// paged-invoices.component.ts
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { CommonModule } from '@angular/common';
import { defineCustomElements } from '@awc-ui/core/loader';
defineCustomElements(window);
interface Invoice { id: string; client: string; amount: number; status: string; }
@Component({
selector: 'app-paged-invoices',
standalone: true,
imports: [CommonModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `
<md-table-container variant="outlined" max-height="318px">
<md-table
frozen-header
[attr.row-count]="invoices.length"
[attr.row-offset]="page * perPage"
column-template="2fr 1fr 1fr"
label="Paged invoices"
>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row *ngFor="let row of pageRows; trackBy: trackById" [attr.value]="row.id">
<md-table-cell head scope="row">{{ row.client }}</md-table-cell>
<md-table-cell numeric>{{ row.amount | currency }}</md-table-cell>
<md-table-cell>{{ row.status }}</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
<md-table-pagination
slot="bottom"
[attr.count]="invoices.length"
[attr.page]="page"
[attr.rows-per-page]="perPage"
(mdPageChange)="page = $any($event).detail.page"
(mdRowsPerPageChange)="onRowsPerPage($event)"
></md-table-pagination>
</md-table-container>
`,
})
export class PagedInvoicesComponent {
invoices: Invoice[] = [
{ id: 'p1', client: 'Acme Corp', amount: 1200, status: 'Paid' },
{ id: 'p2', client: 'Globex', amount: 3120, status: 'Pending' },
{ id: 'p3', client: 'Initech', amount: 860, status: 'Paid' },
{ id: 'p4', client: 'Umbrella', amount: 2450, status: 'Draft' },
{ id: 'p5', client: 'Soylent', amount: 1975, status: 'Paid' },
{ id: 'p6', client: 'Hooli', amount: 4300, status: 'Pending' },
{ id: 'p7', client: 'Stark Industries', amount: 8120, status: 'Paid' },
{ id: 'p8', client: 'Wayne Enterprises', amount: 6640, status: 'Draft' },
{ id: 'p9', client: 'Cyberdyne', amount: 2010, status: 'Paid' },
{ id: 'p10', client: 'Tyrell Corp', amount: 3780, status: 'Pending' },
];
page = 0;
perPage = 5;
get pageRows(): Invoice[] {
return this.invoices.slice(this.page * this.perPage, (this.page + 1) * this.perPage);
}
onRowsPerPage(event: Event) {
this.perPage = (event as CustomEvent<{ rowsPerPage: number }>).detail.rowsPerPage;
this.page = 0;
}
trackById = (_: number, row: Invoice) => row.id;
}<script setup>
import { computed, ref } from 'vue';
import '@awc-ui/core/define';
const INVOICES = [
{ id: 'p1', client: 'Acme Corp', amount: 1200, status: 'Paid' },
{ id: 'p2', client: 'Globex', amount: 3120, status: 'Pending' },
{ id: 'p3', client: 'Initech', amount: 860, status: 'Paid' },
{ id: 'p4', client: 'Umbrella', amount: 2450, status: 'Draft' },
{ id: 'p5', client: 'Soylent', amount: 1975, status: 'Paid' },
{ id: 'p6', client: 'Hooli', amount: 4300, status: 'Pending' },
{ id: 'p7', client: 'Stark Industries', amount: 8120, status: 'Paid' },
{ id: 'p8', client: 'Wayne Enterprises', amount: 6640, status: 'Draft' },
{ id: 'p9', client: 'Cyberdyne', amount: 2010, status: 'Paid' },
{ id: 'p10', client: 'Tyrell Corp', amount: 3780, status: 'Pending' },
];
const money = (n) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
const page = ref(0);
const perPage = ref(5);
const pageRows = computed(() =>
INVOICES.slice(page.value * perPage.value, (page.value + 1) * perPage.value),
);
</script>
<template>
<md-table-container variant="outlined" max-height="318px">
<md-table
frozen-header
:row-count="INVOICES.length"
:row-offset="page * perPage"
column-template="2fr 1fr 1fr"
label="Paged invoices"
>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row v-for="row in pageRows" :key="row.id" :value="row.id">
<md-table-cell head scope="row">{{ row.client }}</md-table-cell>
<md-table-cell numeric>{{ money(row.amount) }}</md-table-cell>
<md-table-cell>{{ row.status }}</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
<md-table-pagination
slot="bottom"
:count="INVOICES.length"
:page="page"
:rows-per-page="perPage"
@mdPageChange="page = $event.detail.page"
@mdRowsPerPageChange="perPage = $event.detail.rowsPerPage; page = 0"
></md-table-pagination>
</md-table-container>
</template><script>
import '@awc-ui/core/define';
const INVOICES = [
{ id: 'p1', client: 'Acme Corp', amount: 1200, status: 'Paid' },
{ id: 'p2', client: 'Globex', amount: 3120, status: 'Pending' },
{ id: 'p3', client: 'Initech', amount: 860, status: 'Paid' },
{ id: 'p4', client: 'Umbrella', amount: 2450, status: 'Draft' },
{ id: 'p5', client: 'Soylent', amount: 1975, status: 'Paid' },
{ id: 'p6', client: 'Hooli', amount: 4300, status: 'Pending' },
{ id: 'p7', client: 'Stark Industries', amount: 8120, status: 'Paid' },
{ id: 'p8', client: 'Wayne Enterprises', amount: 6640, status: 'Draft' },
{ id: 'p9', client: 'Cyberdyne', amount: 2010, status: 'Paid' },
{ id: 'p10', client: 'Tyrell Corp', amount: 3780, status: 'Pending' },
];
const money = (n) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
let page = 0;
let perPage = 5;
$: pageRows = INVOICES.slice(page * perPage, (page + 1) * perPage);
</script>
<md-table-container variant="outlined" max-height="318px">
<md-table
frozen-header
row-count={INVOICES.length}
row-offset={page * perPage}
column-template="2fr 1fr 1fr"
label="Paged invoices"
>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
{#each pageRows as row (row.id)}
<md-table-row value={row.id}>
<md-table-cell head scope="row">{row.client}</md-table-cell>
<md-table-cell numeric>{money(row.amount)}</md-table-cell>
<md-table-cell>{row.status}</md-table-cell>
</md-table-row>
{/each}
</md-table-body>
</md-table>
<md-table-pagination
slot="bottom"
count={INVOICES.length}
{page}
rows-per-page={perPage}
on:mdPageChange={(e) => (page = e.detail.page)}
on:mdRowsPerPageChange={(e) => { perPage = e.detail.rowsPerPage; page = 0; }}
></md-table-pagination>
</md-table-container>Server-side paging is the same wiring with the data fetch in the middle. The first load has nothing to keep on screen, so it shows the skeleton; every page change after that keeps the current rows up under the overlay line while the next page is fetched:
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined" max-height="318px">
<md-table id="srv-table" frozen-header row-count="47" column-template="2fr 1fr 1fr" label="Server-paged employees" loading loading-mode="skeleton" loading-rows="5">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col"><md-table-sort-label column="name">Name</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col">Role</md-table-cell>
<md-table-cell head scope="col" numeric><md-table-sort-label column="salary">Salary</md-table-sort-label></md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body id="srv-body"></md-table-body>
</md-table>
<md-table-pagination id="srv-pag" slot="bottom" count="47" page="0" rows-per-page="5"></md-table-pagination>
</md-table-container>
<script type="module">
var table = document.getElementById('srv-table');
var body = document.getElementById('srv-body');
var pag = document.getElementById('srv-pag');
// The whole dataset "lives on the server" — the client only ever holds a page.
var DATA = [];
for (var i = 0; i < 47; i++) {
DATA.push({
name: 'Employee ' + String(i + 1).padStart(2, '0'),
role: ['Engineer', 'Designer', 'Product Manager', 'Analyst'][i % 4],
salary: 90000 + ((i * 37) % 40) * 2500,
});
}
var page = 0, perPage = 5, sortBy = '', sortOrder = 'none', timer = 0;
var money = function (n) { return '$' + n.toLocaleString('en-US'); };
var fetchPage = function () {
// Nothing on screen yet -> skeleton. Rows already up -> overlay, so a page
// change never blanks the table.
table.loadingMode = body.querySelector('md-table-row') ? 'overlay' : 'skeleton';
table.loading = true;
clearTimeout(timer);
timer = setTimeout(function () {
var rows = DATA.slice();
if (sortBy && sortOrder !== 'none') {
var dir = sortOrder === 'desc' ? -1 : 1;
rows.sort(function (a, b) {
var av = a[sortBy], bv = b[sortBy];
return (typeof av === 'number' ? av - bv : String(av).localeCompare(String(bv))) * dir;
});
}
var start = page * perPage;
body.innerHTML = rows.slice(start, start + perPage).map(function (r) {
return '<md-table-row><md-table-cell head scope="row">' + r.name +
'</md-table-cell><md-table-cell>' + r.role +
'</md-table-cell><md-table-cell numeric>' + money(r.salary) + '</md-table-cell></md-table-row>';
}).join('');
table.setAttribute('row-offset', String(start));
table.loading = false;
}, 700);
};
pag.addEventListener('mdPageChange', function (e) { page = e.detail.page; fetchPage(); });
pag.addEventListener('mdRowsPerPageChange', function (e) { perPage = e.detail.rowsPerPage; page = 0; pag.page = 0; fetchPage(); });
// Sorting is server-side too, so it re-sorts the whole set and returns to page 1.
table.addEventListener('mdSortChange', function (e) {
sortBy = e.detail.column; sortOrder = e.detail.order;
page = 0; pag.page = 0; fetchPage();
});
fetchPage();
</script>import { useCallback, useEffect, useRef, useState } from 'react';
import {
MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead,
MdTablePagination, MdTableRow, MdTableSortLabel,
} from '@awc-ui/react';
const DATA = Array.from({ length: 47 }, (_, i) => ({
name: `Employee ${String(i + 1).padStart(2, '0')}`,
role: ['Engineer', 'Designer', 'Product Manager', 'Analyst'][i % 4],
salary: 90000 + ((i * 37) % 40) * 2500,
}));
// Stand-in for your API. Sorting and slicing happen "on the server".
const fetchPage = async ({ page, perPage, sortBy, sortOrder }) => {
const rows = [...DATA];
if (sortBy && sortOrder !== 'none') {
const dir = sortOrder === 'desc' ? -1 : 1;
rows.sort((a, b) => {
const av = a[sortBy];
const bv = b[sortBy];
return (typeof av === 'number' ? av - bv : String(av).localeCompare(String(bv))) * dir;
});
}
await new Promise((r) => setTimeout(r, 700)); // latency
return { rows: rows.slice(page * perPage, (page + 1) * perPage), total: DATA.length };
};
const money = (n) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
export function ServerPagedEmployees() {
const [rows, setRows] = useState([]);
const [total, setTotal] = useState(0);
const [page, setPage] = useState(0);
const [perPage, setPerPage] = useState(5);
const [sort, setSort] = useState({ column: '', order: 'none' });
const [loading, setLoading] = useState(true);
const hasRows = useRef(false);
const load = useCallback(async () => {
setLoading(true);
const res = await fetchPage({ page, perPage, sortBy: sort.column, sortOrder: sort.order });
setRows(res.rows);
setTotal(res.total);
hasRows.current = true;
setLoading(false);
}, [page, perPage, sort]);
useEffect(() => { load(); }, [load]);
return (
<MdTableContainer variant="outlined" maxHeight="318px">
<MdTable
frozenHeader
rowCount={total}
rowOffset={page * perPage}
columnTemplate="2fr 1fr 1fr"
label="Server-paged employees"
loading={loading}
// First load has nothing to keep on screen -> skeleton. Afterwards the
// overlay keeps the current page readable while the next one loads.
loadingMode={hasRows.current ? 'overlay' : 'skeleton'}
loadingRows={perPage}
onMdSortChange={(e) => { setSort(e.detail); setPage(0); }}
>
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col"><MdTableSortLabel column="name">Name</MdTableSortLabel></MdTableCell>
<MdTableCell head scope="col">Role</MdTableCell>
<MdTableCell head scope="col" numeric><MdTableSortLabel column="salary">Salary</MdTableSortLabel></MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
{rows.map((row) => (
<MdTableRow key={row.name}>
<MdTableCell head scope="row">{row.name}</MdTableCell>
<MdTableCell>{row.role}</MdTableCell>
<MdTableCell numeric>{money(row.salary)}</MdTableCell>
</MdTableRow>
))}
</MdTableBody>
</MdTable>
<MdTablePagination
slot="bottom"
count={total}
page={page}
rowsPerPage={perPage}
onMdPageChange={(e) => setPage(e.detail.page)}
onMdRowsPerPageChange={(e) => { setPerPage(e.detail.rowsPerPage); setPage(0); }}
/>
</MdTableContainer>
);
}// server-paged-employees.component.ts
import { Component, CUSTOM_ELEMENTS_SCHEMA, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { defineCustomElements } from '@awc-ui/core/loader';
defineCustomElements(window);
interface Employee { name: string; role: string; salary: number; }
@Component({
selector: 'app-server-paged-employees',
standalone: true,
imports: [CommonModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `
<md-table-container variant="outlined" max-height="318px">
<md-table
frozen-header
[attr.row-count]="total"
[attr.row-offset]="page * perPage"
column-template="2fr 1fr 1fr"
label="Server-paged employees"
[attr.loading]="loading ? '' : null"
[attr.loading-mode]="hasRows ? 'overlay' : 'skeleton'"
[attr.loading-rows]="perPage"
(mdSortChange)="onSort($event)"
>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col"><md-table-sort-label column="name">Name</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col">Role</md-table-cell>
<md-table-cell head scope="col" numeric><md-table-sort-label column="salary">Salary</md-table-sort-label></md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row *ngFor="let row of rows; trackBy: trackByName">
<md-table-cell head scope="row">{{ row.name }}</md-table-cell>
<md-table-cell>{{ row.role }}</md-table-cell>
<md-table-cell numeric>{{ row.salary | currency }}</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
<md-table-pagination
slot="bottom"
[attr.count]="total"
[attr.page]="page"
[attr.rows-per-page]="perPage"
(mdPageChange)="page = $any($event).detail.page; load()"
(mdRowsPerPageChange)="perPage = $any($event).detail.rowsPerPage; page = 0; load()"
></md-table-pagination>
</md-table-container>
`,
})
export class ServerPagedEmployeesComponent implements OnInit {
rows: Employee[] = [];
total = 0;
page = 0;
perPage = 5;
sortBy = '';
sortOrder = 'none';
loading = true;
hasRows = false;
ngOnInit() { this.load(); }
async load() {
this.loading = true;
// Replace with your HTTP call — the table only cares about loading + rows.
const { rows, total } = await fetchPage({
page: this.page, perPage: this.perPage, sortBy: this.sortBy, sortOrder: this.sortOrder,
});
this.rows = rows;
this.total = total;
this.hasRows = true;
this.loading = false;
}
onSort(event: Event) {
const { column, order } = (event as CustomEvent<{ column: string; order: string }>).detail;
this.sortBy = column;
this.sortOrder = order;
this.page = 0;
this.load();
}
trackByName = (_: number, row: Employee) => row.name;
}<script setup>
import { onMounted, ref, watch } from 'vue';
import '@awc-ui/core/define';
const DATA = Array.from({ length: 47 }, (_, i) => ({
name: `Employee ${String(i + 1).padStart(2, '0')}`,
role: ['Engineer', 'Designer', 'Product Manager', 'Analyst'][i % 4],
salary: 90000 + ((i * 37) % 40) * 2500,
}));
// Stand-in for your API. Sorting and slicing happen "on the server".
const fetchPage = async ({ page, perPage, sortBy, sortOrder }) => {
const rows = [...DATA];
if (sortBy && sortOrder !== 'none') {
const dir = sortOrder === 'desc' ? -1 : 1;
rows.sort((a, b) => {
const av = a[sortBy];
const bv = b[sortBy];
return (typeof av === 'number' ? av - bv : String(av).localeCompare(String(bv))) * dir;
});
}
await new Promise((r) => setTimeout(r, 700)); // latency
return { rows: rows.slice(page * perPage, (page + 1) * perPage), total: DATA.length };
};
const money = (n) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
const rows = ref([]);
const total = ref(0);
const page = ref(0);
const perPage = ref(5);
const sort = ref({ column: '', order: 'none' });
const loading = ref(true);
const hasRows = ref(false);
const load = async () => {
loading.value = true;
const res = await fetchPage({
page: page.value, perPage: perPage.value, sortBy: sort.value.column, sortOrder: sort.value.order,
});
rows.value = res.rows;
total.value = res.total;
hasRows.value = true;
loading.value = false;
};
onMounted(load);
watch([page, perPage, sort], load);
</script>
<template>
<md-table-container variant="outlined" max-height="318px">
<md-table
frozen-header
:row-count="total"
:row-offset="page * perPage"
column-template="2fr 1fr 1fr"
label="Server-paged employees"
:loading="loading"
:loading-mode="hasRows ? 'overlay' : 'skeleton'"
:loading-rows="perPage"
@mdSortChange="sort = $event.detail; page = 0"
>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col"><md-table-sort-label column="name">Name</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col">Role</md-table-cell>
<md-table-cell head scope="col" numeric><md-table-sort-label column="salary">Salary</md-table-sort-label></md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row v-for="row in rows" :key="row.name">
<md-table-cell head scope="row">{{ row.name }}</md-table-cell>
<md-table-cell>{{ row.role }}</md-table-cell>
<md-table-cell numeric>{{ money(row.salary) }}</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
<md-table-pagination
slot="bottom"
:count="total"
:page="page"
:rows-per-page="perPage"
@mdPageChange="page = $event.detail.page"
@mdRowsPerPageChange="perPage = $event.detail.rowsPerPage; page = 0"
></md-table-pagination>
</md-table-container>
</template><script>
import { onMount } from 'svelte';
import '@awc-ui/core/define';
const DATA = Array.from({ length: 47 }, (_, i) => ({
name: `Employee ${String(i + 1).padStart(2, '0')}`,
role: ['Engineer', 'Designer', 'Product Manager', 'Analyst'][i % 4],
salary: 90000 + ((i * 37) % 40) * 2500,
}));
// Stand-in for your API. Sorting and slicing happen "on the server".
const fetchPage = async ({ page, perPage, sortBy, sortOrder }) => {
const rows = [...DATA];
if (sortBy && sortOrder !== 'none') {
const dir = sortOrder === 'desc' ? -1 : 1;
rows.sort((a, b) => {
const av = a[sortBy];
const bv = b[sortBy];
return (typeof av === 'number' ? av - bv : String(av).localeCompare(String(bv))) * dir;
});
}
await new Promise((r) => setTimeout(r, 700)); // latency
return { rows: rows.slice(page * perPage, (page + 1) * perPage), total: DATA.length };
};
const money = (n) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
let rows = [];
let total = 0;
let page = 0;
let perPage = 5;
let sort = { column: '', order: 'none' };
let loading = true;
let hasRows = false;
const load = async () => {
loading = true;
const res = await fetchPage({ page, perPage, sortBy: sort.column, sortOrder: sort.order });
rows = res.rows;
total = res.total;
hasRows = true;
loading = false;
};
onMount(load);
</script>
<md-table-container variant="outlined" max-height="318px">
<md-table
frozen-header
row-count={total}
row-offset={page * perPage}
column-template="2fr 1fr 1fr"
label="Server-paged employees"
{loading}
loading-mode={hasRows ? 'overlay' : 'skeleton'}
loading-rows={perPage}
on:mdSortChange={(e) => { sort = e.detail; page = 0; load(); }}
>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col"><md-table-sort-label column="name">Name</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col">Role</md-table-cell>
<md-table-cell head scope="col" numeric><md-table-sort-label column="salary">Salary</md-table-sort-label></md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
{#each rows as row (row.name)}
<md-table-row>
<md-table-cell head scope="row">{row.name}</md-table-cell>
<md-table-cell>{row.role}</md-table-cell>
<md-table-cell numeric>{money(row.salary)}</md-table-cell>
</md-table-row>
{/each}
</md-table-body>
</md-table>
<md-table-pagination
slot="bottom"
count={total}
{page}
rows-per-page={perPage}
on:mdPageChange={(e) => { page = e.detail.page; load(); }}
on:mdRowsPerPageChange={(e) => { perPage = e.detail.rowsPerPage; page = 0; load(); }}
></md-table-pagination>
</md-table-container>rows-per-page must be one of rows-per-page-options (default
5,10,25,50) — set it to a value outside that list and the selector renders
blank, because no option matches. Change the list rather than the value if
you need a different page size.
Every piece above, in one table: a toolbar that switches to selection mode, a quick filter, sortable columns, multiple selection with a select-all, a frozen header, a pinned first column and pagination — all of it driven by the same events documented in each section.
<!-- index.html <head> — the icon font the components draw from -->
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&display=swap">
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined" max-height="420px">
<md-table-toolbar id="fdg-toolbar" slot="top" headline="Employees" supporting-text="12 people" auto-bind>
<md-icon-button slot="actions" id="fdg-filter-btn" icon="filter_list" aria-label="Filter employees"></md-icon-button>
<md-icon-button slot="selection-actions" icon="delete" aria-label="Delete selected"></md-icon-button>
<md-icon-button slot="selection-actions" icon="archive" aria-label="Archive selected"></md-icon-button>
</md-table-toolbar>
<div id="fdg-filter" hidden style="padding: 8px 16px 12px;">
<md-text-field id="fdg-query" variant="outlined" label="Filter" density="-2" style="inline-size: 260px;"><span slot="leading-icon" class="material-symbols-outlined">search</span></md-text-field>
</div>
<md-table id="fdg-table" frozen-header selection="multiple" row-count="12" column-template="auto 1.6fr 1.4fr 1fr 1fr" label="Employees">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox"><md-checkbox id="fdg-all" aria-label="Select all rows"></md-checkbox></md-table-cell>
<md-table-cell head scope="col"><md-table-sort-label column="name">Name</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col"><md-table-sort-label column="role">Role</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col"><md-table-sort-label column="team">Team</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col" numeric><md-table-sort-label column="salary">Salary</md-table-sort-label></md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body id="fdg-body">
<md-table-row value="g1"><md-table-cell padding="checkbox"><md-checkbox aria-label="Select Ada Lovelace"></md-checkbox></md-table-cell><md-table-cell head scope="row" data-col="name">Ada Lovelace</md-table-cell><md-table-cell data-col="role">Mathematician</md-table-cell><md-table-cell data-col="team">Engineering</md-table-cell><md-table-cell numeric data-col="salary">$120,000</md-table-cell></md-table-row>
<md-table-row value="g2"><md-table-cell padding="checkbox"><md-checkbox aria-label="Select Alan Turing"></md-checkbox></md-table-cell><md-table-cell head scope="row" data-col="name">Alan Turing</md-table-cell><md-table-cell data-col="role">Cryptanalyst</md-table-cell><md-table-cell data-col="team">Security</md-table-cell><md-table-cell numeric data-col="salary">$145,000</md-table-cell></md-table-row>
<md-table-row value="g3"><md-table-cell padding="checkbox"><md-checkbox aria-label="Select Grace Hopper"></md-checkbox></md-table-cell><md-table-cell head scope="row" data-col="name">Grace Hopper</md-table-cell><md-table-cell data-col="role">Compiler Engineer</md-table-cell><md-table-cell data-col="team">Platform</md-table-cell><md-table-cell numeric data-col="salary">$132,000</md-table-cell></md-table-row>
<md-table-row value="g4"><md-table-cell padding="checkbox"><md-checkbox aria-label="Select Linus Torvalds"></md-checkbox></md-table-cell><md-table-cell head scope="row" data-col="name">Linus Torvalds</md-table-cell><md-table-cell data-col="role">Kernel Engineer</md-table-cell><md-table-cell data-col="team">Infrastructure</md-table-cell><md-table-cell numeric data-col="salary">$168,000</md-table-cell></md-table-row>
<md-table-row value="g5"><md-table-cell padding="checkbox"><md-checkbox aria-label="Select Margaret Hamilton"></md-checkbox></md-table-cell><md-table-cell head scope="row" data-col="name">Margaret Hamilton</md-table-cell><md-table-cell data-col="role">Software Lead</md-table-cell><md-table-cell data-col="team">Avionics</md-table-cell><md-table-cell numeric data-col="salary">$175,000</md-table-cell></md-table-row>
<md-table-row value="g6"><md-table-cell padding="checkbox"><md-checkbox aria-label="Select Donald Knuth"></md-checkbox></md-table-cell><md-table-cell head scope="row" data-col="name">Donald Knuth</md-table-cell><md-table-cell data-col="role">Author / Researcher</md-table-cell><md-table-cell data-col="team">Research</md-table-cell><md-table-cell numeric data-col="salary">$195,000</md-table-cell></md-table-row>
<md-table-row value="g7"><md-table-cell padding="checkbox"><md-checkbox aria-label="Select Barbara Liskov"></md-checkbox></md-table-cell><md-table-cell head scope="row" data-col="name">Barbara Liskov</md-table-cell><md-table-cell data-col="role">Programming Language Designer</md-table-cell><md-table-cell data-col="team">Research</md-table-cell><md-table-cell numeric data-col="salary">$188,000</md-table-cell></md-table-row>
<md-table-row value="g8"><md-table-cell padding="checkbox"><md-checkbox aria-label="Select Katherine Johnson"></md-checkbox></md-table-cell><md-table-cell head scope="row" data-col="name">Katherine Johnson</md-table-cell><md-table-cell data-col="role">Orbital Analyst</md-table-cell><md-table-cell data-col="team">Avionics</md-table-cell><md-table-cell numeric data-col="salary">$140,000</md-table-cell></md-table-row>
<md-table-row value="g9"><md-table-cell padding="checkbox"><md-checkbox aria-label="Select Tim Berners-Lee"></md-checkbox></md-table-cell><md-table-cell head scope="row" data-col="name">Tim Berners-Lee</md-table-cell><md-table-cell data-col="role">Web Architect</md-table-cell><md-table-cell data-col="team">Platform</md-table-cell><md-table-cell numeric data-col="salary">$210,000</md-table-cell></md-table-row>
<md-table-row value="g10"><md-table-cell padding="checkbox"><md-checkbox aria-label="Select Radia Perlman"></md-checkbox></md-table-cell><md-table-cell head scope="row" data-col="name">Radia Perlman</md-table-cell><md-table-cell data-col="role">Network Engineer</md-table-cell><md-table-cell data-col="team">Infrastructure</md-table-cell><md-table-cell numeric data-col="salary">$158,000</md-table-cell></md-table-row>
<md-table-row value="g11"><md-table-cell padding="checkbox"><md-checkbox aria-label="Select Anita Borg"></md-checkbox></md-table-cell><md-table-cell head scope="row" data-col="name">Anita Borg</md-table-cell><md-table-cell data-col="role">Systems Researcher</md-table-cell><md-table-cell data-col="team">Research</md-table-cell><md-table-cell numeric data-col="salary">$150,000</md-table-cell></md-table-row>
<md-table-row value="g12"><md-table-cell padding="checkbox"><md-checkbox aria-label="Select Guido van Rossum"></md-checkbox></md-table-cell><md-table-cell head scope="row" data-col="name">Guido van Rossum</md-table-cell><md-table-cell data-col="role">Language Designer</md-table-cell><md-table-cell data-col="team">Platform</md-table-cell><md-table-cell numeric data-col="salary">$205,000</md-table-cell></md-table-row>
</md-table-body>
</md-table>
<md-table-pagination id="fdg-pag" slot="bottom" count="12" page="0" rows-per-page="5"></md-table-pagination>
</md-table-container>
<script type="module">
var table = document.getElementById('fdg-table');
var body = document.getElementById('fdg-body');
var pag = document.getElementById('fdg-pag');
var query = '', page = 0, perPage = 5, sortBy = '', sortOrder = 'none';
var cell = function (row, col) { return (row.querySelector('[data-col="' + col + '"]').textContent || '').trim(); };
var num = function (v) { return parseFloat(v.replace(/[^0-9.-]/g, '')); };
// One render pass owns filter + sort + page. Rows are reordered and hidden,
// never rebuilt, so selection survives every one of those changes.
var render = function () {
var rows = Array.prototype.slice.call(body.querySelectorAll('md-table-row'));
if (sortBy && sortOrder !== 'none') {
var dir = sortOrder === 'desc' ? -1 : 1;
rows.sort(function (a, b) {
var av = cell(a, sortBy), bv = cell(b, sortBy);
return (sortBy === 'salary' ? num(av) - num(bv) : av.localeCompare(bv)) * dir;
});
rows.forEach(function (r) { body.appendChild(r); });
}
var q = query.trim().toLowerCase();
var matches = rows.filter(function (r) { return !q || r.textContent.toLowerCase().indexOf(q) > -1; });
pag.count = matches.length;
if (page * perPage >= matches.length) { page = 0; pag.page = 0; }
rows.forEach(function (r) { r.style.display = 'none'; });
matches.slice(page * perPage, page * perPage + perPage).forEach(function (r) { r.style.display = ''; });
table.empty = matches.length === 0;
// No row-offset: every row stays in the DOM (hidden, not removed), so the
// table's own body index is already the absolute one. row-offset is for the
// case where only the current page exists in the DOM.
};
// The select-all checkbox needs no listener — selection="multiple" makes the
// table handle slotted checkboxes, head box included.
table.addEventListener('mdSortChange', function (e) { sortBy = e.detail.column; sortOrder = e.detail.order; render(); });
pag.addEventListener('mdPageChange', function (e) { page = e.detail.page; render(); });
pag.addEventListener('mdRowsPerPageChange', function (e) { perPage = e.detail.rowsPerPage; page = 0; render(); });
var panel = document.getElementById('fdg-filter');
var field = document.getElementById('fdg-query');
document.getElementById('fdg-filter-btn').addEventListener('click', function () {
panel.hidden = !panel.hidden;
if (!panel.hidden) field.setFocus();
});
// md-text-field emits mdInput with the value AS the detail (a string) — not
// wrapped in an object.
field.addEventListener('mdInput', function (e) { query = e.detail; page = 0; pag.page = 0; render(); });
render();
</script>import { useMemo, useRef, useState } from 'react';
import {
MdCheckbox, MdIconButton, MdTable, MdTableBody, MdTableCell, MdTableContainer,
MdTableHead, MdTablePagination, MdTableRow, MdTableSortLabel, MdTableToolbar, MdTextField,
} from '@awc-ui/react';
const PEOPLE = [
{ id: 'g1', name: 'Ada Lovelace', role: 'Mathematician', team: 'Engineering', salary: 120000 },
{ id: 'g2', name: 'Alan Turing', role: 'Cryptanalyst', team: 'Security', salary: 145000 },
{ id: 'g3', name: 'Grace Hopper', role: 'Compiler Engineer', team: 'Platform', salary: 132000 },
{ id: 'g4', name: 'Linus Torvalds', role: 'Kernel Engineer', team: 'Infrastructure', salary: 168000 },
{ id: 'g5', name: 'Margaret Hamilton', role: 'Software Lead', team: 'Avionics', salary: 175000 },
{ id: 'g6', name: 'Donald Knuth', role: 'Author / Researcher', team: 'Research', salary: 195000 },
// … six more …
];
const money = (n) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 });
export function EmployeeGrid() {
const tableRef = useRef(null);
const [query, setQuery] = useState('');
const [showFilter, setShowFilter] = useState(false);
const [sort, setSort] = useState({ column: '', order: 'none' });
const [page, setPage] = useState(0);
const [perPage, setPerPage] = useState(5);
// Filter -> sort -> page, derived in that order from one source of truth.
const filtered = useMemo(() => {
const q = query.trim().toLowerCase();
if (!q) return PEOPLE;
return PEOPLE.filter((p) =>
[p.name, p.role, p.team].some((v) => v.toLowerCase().includes(q)),
);
}, [query]);
const sorted = useMemo(() => {
if (!sort.column || sort.order === 'none') return filtered;
const dir = sort.order === 'desc' ? -1 : 1;
return [...filtered].sort((a, b) => {
const av = a[sort.column], bv = b[sort.column];
return (typeof av === 'number' ? av - bv : String(av).localeCompare(String(bv))) * dir;
});
}, [filtered, sort]);
const pageRows = sorted.slice(page * perPage, (page + 1) * perPage);
return (
<MdTableContainer variant="outlined" maxHeight="420px">
<MdTableToolbar slot="top" headline="Employees" supportingText={`${PEOPLE.length} people`} autoBind>
<MdIconButton
slot="actions"
icon="filter_list"
aria-label="Filter employees"
onClick={() => setShowFilter((v) => !v)}
/>
<MdIconButton slot="selection-actions" icon="delete" aria-label="Delete selected" />
<MdIconButton slot="selection-actions" icon="archive" aria-label="Archive selected" />
</MdTableToolbar>
{showFilter && (
<div style={{ padding: '8px 16px 12px' }}>
<MdTextField
variant="outlined"
label="Filter"
density={-2}
value={query}
// mdInput's detail IS the value (a string), not { value }.
onMdInput={(e) => { setQuery(e.detail); setPage(0); }}
>
<span slot="leading-icon" className="material-symbols-outlined">search</span>
</MdTextField>
</div>
)}
<MdTable
ref={tableRef}
frozenHeader
selection="multiple"
rowCount={sorted.length}
rowOffset={page * perPage}
columnTemplate="auto 1.6fr 1.4fr 1fr 1fr"
label="Employees"
empty={sorted.length === 0}
onMdSortChange={(e) => { setSort(e.detail); setPage(0); }}
>
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head padding="checkbox">
<MdCheckbox aria-label="Select all rows" onMdChange={() => tableRef.current?.toggleSelectAll()} />
</MdTableCell>
<MdTableCell head scope="col"><MdTableSortLabel column="name">Name</MdTableSortLabel></MdTableCell>
<MdTableCell head scope="col"><MdTableSortLabel column="role">Role</MdTableSortLabel></MdTableCell>
<MdTableCell head scope="col"><MdTableSortLabel column="team">Team</MdTableSortLabel></MdTableCell>
<MdTableCell head scope="col" numeric><MdTableSortLabel column="salary">Salary</MdTableSortLabel></MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
{pageRows.map((p) => (
<MdTableRow key={p.id} value={p.id}>
<MdTableCell padding="checkbox"><MdCheckbox aria-label={`Select ${p.name}`} /></MdTableCell>
<MdTableCell head scope="row">{p.name}</MdTableCell>
<MdTableCell>{p.role}</MdTableCell>
<MdTableCell>{p.team}</MdTableCell>
<MdTableCell numeric>{money(p.salary)}</MdTableCell>
</MdTableRow>
))}
</MdTableBody>
<div slot="empty" style={{ padding: 32, textAlign: 'center' }}>No employees match that filter.</div>
</MdTable>
<MdTablePagination
slot="bottom"
count={sorted.length}
page={page}
rowsPerPage={perPage}
onMdPageChange={(e) => setPage(e.detail.page)}
onMdRowsPerPageChange={(e) => { setPerPage(e.detail.rowsPerPage); setPage(0); }}
/>
</MdTableContainer>
);
}// employee-grid.component.ts
import { Component, CUSTOM_ELEMENTS_SCHEMA, ElementRef, ViewChild } from '@angular/core';
import { CommonModule } from '@angular/common';
import { defineCustomElements } from '@awc-ui/core/loader';
defineCustomElements(window);
interface Person { id: string; name: string; role: string; team: string; salary: number; }
@Component({
selector: 'app-employee-grid',
standalone: true,
imports: [CommonModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `
<md-table-container variant="outlined" max-height="420px">
<md-table-toolbar slot="top" headline="Employees" [attr.supporting-text]="people.length + ' people'" auto-bind>
<md-icon-button slot="actions" icon="filter_list" aria-label="Filter employees"
(click)="showFilter = !showFilter"></md-icon-button>
<md-icon-button slot="selection-actions" icon="delete" aria-label="Delete selected"></md-icon-button>
<md-icon-button slot="selection-actions" icon="archive" aria-label="Archive selected"></md-icon-button>
</md-table-toolbar>
<div *ngIf="showFilter" style="padding: 8px 16px 12px;">
<md-text-field variant="outlined" label="Filter" density="-2" (mdInput)="onQuery($event)">
<span slot="leading-icon" class="material-symbols-outlined">search</span>
</md-text-field>
</div>
<md-table
#table
frozen-header
selection="multiple"
[attr.row-count]="sorted.length"
[attr.row-offset]="page * perPage"
column-template="auto 1.6fr 1.4fr 1fr 1fr"
label="Employees"
[attr.empty]="sorted.length === 0 ? '' : null"
(mdSortChange)="onSort($event)"
>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox">
<md-checkbox aria-label="Select all rows" (mdChange)="selectAll()"></md-checkbox>
</md-table-cell>
<md-table-cell head scope="col"><md-table-sort-label column="name">Name</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col"><md-table-sort-label column="role">Role</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col"><md-table-sort-label column="team">Team</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col" numeric><md-table-sort-label column="salary">Salary</md-table-sort-label></md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row *ngFor="let p of pageRows; trackBy: trackById" [attr.value]="p.id">
<md-table-cell padding="checkbox">
<md-checkbox [attr.aria-label]="'Select ' + p.name"></md-checkbox>
</md-table-cell>
<md-table-cell head scope="row">{{ p.name }}</md-table-cell>
<md-table-cell>{{ p.role }}</md-table-cell>
<md-table-cell>{{ p.team }}</md-table-cell>
<md-table-cell numeric>{{ p.salary | currency:'USD':'symbol':'1.0-0' }}</md-table-cell>
</md-table-row>
</md-table-body>
<div slot="empty" style="padding: 32px; text-align: center;">No employees match that filter.</div>
</md-table>
<md-table-pagination
slot="bottom"
[attr.count]="sorted.length"
[attr.page]="page"
[attr.rows-per-page]="perPage"
(mdPageChange)="page = $any($event).detail.page"
(mdRowsPerPageChange)="perPage = $any($event).detail.rowsPerPage; page = 0"
></md-table-pagination>
</md-table-container>
`,
})
export class EmployeeGridComponent {
@ViewChild('table') tableRef!: ElementRef<HTMLElement & { toggleSelectAll(): Promise<void> }>;
people: Person[] = [
{ id: 'g1', name: 'Ada Lovelace', role: 'Mathematician', team: 'Engineering', salary: 120000 },
{ id: 'g2', name: 'Alan Turing', role: 'Cryptanalyst', team: 'Security', salary: 145000 },
{ id: 'g3', name: 'Grace Hopper', role: 'Compiler Engineer', team: 'Platform', salary: 132000 },
{ id: 'g4', name: 'Linus Torvalds', role: 'Kernel Engineer', team: 'Infrastructure', salary: 168000 },
{ id: 'g5', name: 'Margaret Hamilton', role: 'Software Lead', team: 'Avionics', salary: 175000 },
{ id: 'g6', name: 'Donald Knuth', role: 'Author / Researcher', team: 'Research', salary: 195000 },
];
query = '';
showFilter = false;
sortBy = '';
sortOrder = 'none';
page = 0;
perPage = 5;
get filtered(): Person[] {
const q = this.query.trim().toLowerCase();
if (!q) return this.people;
return this.people.filter((p) =>
[p.name, p.role, p.team].some((v) => v.toLowerCase().includes(q)));
}
get sorted(): Person[] {
if (!this.sortBy || this.sortOrder === 'none') return this.filtered;
const dir = this.sortOrder === 'desc' ? -1 : 1;
return [...this.filtered].sort((a, b) => {
const av = a[this.sortBy as keyof Person];
const bv = b[this.sortBy as keyof Person];
return (typeof av === 'number' && typeof bv === 'number'
? av - bv : String(av).localeCompare(String(bv))) * dir;
});
}
get pageRows(): Person[] {
return this.sorted.slice(this.page * this.perPage, (this.page + 1) * this.perPage);
}
onQuery(event: Event) {
// mdInput's detail IS the value (a string), not { value }.
this.query = (event as CustomEvent<string>).detail;
this.page = 0;
}
onSort(event: Event) {
const { column, order } = (event as CustomEvent<{ column: string; order: string }>).detail;
this.sortBy = column;
this.sortOrder = order;
this.page = 0;
}
selectAll() { void this.tableRef.nativeElement.toggleSelectAll(); }
trackById = (_: number, p: Person) => p.id;
}<script setup>
import { computed, ref } from 'vue';
import '@awc-ui/core/define';
const PEOPLE = [
{ id: 'g1', name: 'Ada Lovelace', role: 'Mathematician', team: 'Engineering', salary: 120000 },
{ id: 'g2', name: 'Alan Turing', role: 'Cryptanalyst', team: 'Security', salary: 145000 },
{ id: 'g3', name: 'Grace Hopper', role: 'Compiler Engineer', team: 'Platform', salary: 132000 },
{ id: 'g4', name: 'Linus Torvalds', role: 'Kernel Engineer', team: 'Infrastructure', salary: 168000 },
{ id: 'g5', name: 'Margaret Hamilton', role: 'Software Lead', team: 'Avionics', salary: 175000 },
{ id: 'g6', name: 'Donald Knuth', role: 'Author / Researcher', team: 'Research', salary: 195000 },
// … six more …
];
const money = (n) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 });
const table = ref(null);
const query = ref('');
const showFilter = ref(false);
const sort = ref({ column: '', order: 'none' });
const page = ref(0);
const perPage = ref(5);
// Filter -> sort -> page, all derived from one source of truth.
const filtered = computed(() => {
const q = query.value.trim().toLowerCase();
if (!q) return PEOPLE;
return PEOPLE.filter((p) => [p.name, p.role, p.team].some((v) => v.toLowerCase().includes(q)));
});
const sorted = computed(() => {
if (!sort.value.column || sort.value.order === 'none') return filtered.value;
const dir = sort.value.order === 'desc' ? -1 : 1;
return [...filtered.value].sort((a, b) => {
const av = a[sort.value.column], bv = b[sort.value.column];
return (typeof av === 'number' ? av - bv : String(av).localeCompare(String(bv))) * dir;
});
});
const pageRows = computed(() =>
sorted.value.slice(page.value * perPage.value, (page.value + 1) * perPage.value));
</script>
<template>
<md-table-container variant="outlined" max-height="420px">
<md-table-toolbar slot="top" headline="Employees" :supporting-text="PEOPLE.length + ' people'" auto-bind>
<md-icon-button slot="actions" icon="filter_list" aria-label="Filter employees"
@click="showFilter = !showFilter"></md-icon-button>
<md-icon-button slot="selection-actions" icon="delete" aria-label="Delete selected"></md-icon-button>
<md-icon-button slot="selection-actions" icon="archive" aria-label="Archive selected"></md-icon-button>
</md-table-toolbar>
<div v-if="showFilter" style="padding: 8px 16px 12px;">
<md-text-field variant="outlined" label="Filter" density="-2"
@mdInput="query = $event.detail; page = 0">
<span slot="leading-icon" class="material-symbols-outlined">search</span>
</md-text-field>
</div>
<md-table
ref="table"
frozen-header
selection="multiple"
:row-count="sorted.length"
:row-offset="page * perPage"
column-template="auto 1.6fr 1.4fr 1fr 1fr"
label="Employees"
:empty="sorted.length === 0"
@mdSortChange="sort = $event.detail; page = 0"
>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox">
<md-checkbox aria-label="Select all rows" @mdChange="table.toggleSelectAll()"></md-checkbox>
</md-table-cell>
<md-table-cell head scope="col"><md-table-sort-label column="name">Name</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col"><md-table-sort-label column="role">Role</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col"><md-table-sort-label column="team">Team</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col" numeric><md-table-sort-label column="salary">Salary</md-table-sort-label></md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row v-for="p in pageRows" :key="p.id" :value="p.id">
<md-table-cell padding="checkbox"><md-checkbox :aria-label="'Select ' + p.name"></md-checkbox></md-table-cell>
<md-table-cell head scope="row">{{ p.name }}</md-table-cell>
<md-table-cell>{{ p.role }}</md-table-cell>
<md-table-cell>{{ p.team }}</md-table-cell>
<md-table-cell numeric>{{ money(p.salary) }}</md-table-cell>
</md-table-row>
</md-table-body>
<div slot="empty" style="padding: 32px; text-align: center;">No employees match that filter.</div>
</md-table>
<md-table-pagination
slot="bottom"
:count="sorted.length"
:page="page"
:rows-per-page="perPage"
@mdPageChange="page = $event.detail.page"
@mdRowsPerPageChange="perPage = $event.detail.rowsPerPage; page = 0"
></md-table-pagination>
</md-table-container>
</template><script>
import '@awc-ui/core/define';
const PEOPLE = [
{ id: 'g1', name: 'Ada Lovelace', role: 'Mathematician', team: 'Engineering', salary: 120000 },
{ id: 'g2', name: 'Alan Turing', role: 'Cryptanalyst', team: 'Security', salary: 145000 },
{ id: 'g3', name: 'Grace Hopper', role: 'Compiler Engineer', team: 'Platform', salary: 132000 },
{ id: 'g4', name: 'Linus Torvalds', role: 'Kernel Engineer', team: 'Infrastructure', salary: 168000 },
{ id: 'g5', name: 'Margaret Hamilton', role: 'Software Lead', team: 'Avionics', salary: 175000 },
{ id: 'g6', name: 'Donald Knuth', role: 'Author / Researcher', team: 'Research', salary: 195000 },
// … six more …
];
const money = (n) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 });
let table;
let query = '';
let showFilter = false;
let sort = { column: '', order: 'none' };
let page = 0;
let perPage = 5;
// Filter -> sort -> page, all reactive from one source of truth.
$: filtered = query.trim()
? PEOPLE.filter((p) => [p.name, p.role, p.team]
.some((v) => v.toLowerCase().includes(query.trim().toLowerCase())))
: PEOPLE;
$: sorted = !sort.column || sort.order === 'none'
? filtered
: [...filtered].sort((a, b) => {
const dir = sort.order === 'desc' ? -1 : 1;
const av = a[sort.column], bv = b[sort.column];
return (typeof av === 'number' ? av - bv : String(av).localeCompare(String(bv))) * dir;
});
$: pageRows = sorted.slice(page * perPage, (page + 1) * perPage);
</script>
<md-table-container variant="outlined" max-height="420px">
<md-table-toolbar slot="top" headline="Employees" supporting-text={PEOPLE.length + ' people'} auto-bind>
<md-icon-button slot="actions" icon="filter_list" aria-label="Filter employees"
on:click={() => (showFilter = !showFilter)}></md-icon-button>
<md-icon-button slot="selection-actions" icon="delete" aria-label="Delete selected"></md-icon-button>
<md-icon-button slot="selection-actions" icon="archive" aria-label="Archive selected"></md-icon-button>
</md-table-toolbar>
{#if showFilter}
<div style="padding: 8px 16px 12px;">
<md-text-field variant="outlined" label="Filter" density="-2"
on:mdInput={(e) => { query = e.detail; page = 0; }}>
<span slot="leading-icon" class="material-symbols-outlined">search</span>
</md-text-field>
</div>
{/if}
<md-table
bind:this={table}
frozen-header
selection="multiple"
row-count={sorted.length}
row-offset={page * perPage}
column-template="auto 1.6fr 1.4fr 1fr 1fr"
label="Employees"
empty={sorted.length === 0}
on:mdSortChange={(e) => { sort = e.detail; page = 0; }}
>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox">
<md-checkbox aria-label="Select all rows" on:mdChange={() => table.toggleSelectAll()}></md-checkbox>
</md-table-cell>
<md-table-cell head scope="col"><md-table-sort-label column="name">Name</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col"><md-table-sort-label column="role">Role</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col"><md-table-sort-label column="team">Team</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col" numeric><md-table-sort-label column="salary">Salary</md-table-sort-label></md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
{#each pageRows as p (p.id)}
<md-table-row value={p.id}>
<md-table-cell padding="checkbox"><md-checkbox aria-label={'Select ' + p.name}></md-checkbox></md-table-cell>
<md-table-cell head scope="row">{p.name}</md-table-cell>
<md-table-cell>{p.role}</md-table-cell>
<md-table-cell>{p.team}</md-table-cell>
<md-table-cell numeric>{money(p.salary)}</md-table-cell>
</md-table-row>
{/each}
</md-table-body>
<div slot="empty" style="padding: 32px; text-align: center;">No employees match that filter.</div>
</md-table>
<md-table-pagination
slot="bottom"
count={sorted.length}
{page}
rows-per-page={perPage}
on:mdPageChange={(e) => (page = e.detail.page)}
on:mdRowsPerPageChange={(e) => { perPage = e.detail.rowsPerPage; page = 0; }}
></md-table-pagination>
</md-table-container>| Event | Cancelable | Detail | Fires |
|---|---|---|---|
mdSortChange | no | MdTableSortState | After a md-table-sort-label is clicked, or setSort() runs |
mdSelectionChange | no | MdTableSelectionState | Whenever the row selection changes |
mdPinChange | no | { column, side } — side is 'start' | 'end' | null | A column is pinned / unpinned via pinColumn() |
mdColumnVisibilityChange | no | { column, visible, hidden } | setColumnVisibility() runs |
mdScroll | no | { scrollLeft, scrollTop } | rAF-throttled scroll of the frozen body scroller |
interface MdTableSortState { column: string; // '' when there is no sort order: 'asc' | 'desc' | 'none';}
interface MdTableSelectionState { count: number; // selected rows total: number; // selectable rendered rows values: string[]; // each selected row's value / id all: boolean; indeterminate: boolean;}All five events bubble and are composed, so one delegated listener on an
ancestor catches them. Sort a column or tick a row below and watch the two you
will actually wire — mdSortChange and mdSelectionChange — arrive:
Sort a column, or tick a row — the events land here.
<md-table-container variant="outlined">
<md-table id="t" column-template="auto 2fr 1fr" selection="multiple" label="Invoices">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox"><md-checkbox id="all" aria-label="Select all rows"></md-checkbox></md-table-cell>
<md-table-cell head scope="col"><md-table-sort-label column="client">Client</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col" numeric><md-table-sort-label column="amount">Amount</md-table-sort-label></md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body id="rows"></md-table-body>
</md-table>
</md-table-container>
<script type="module">
import '@awc-ui/core/define';
const table = document.getElementById('t');
// No checkbox listener needed: selection="multiple" makes the table handle
// the slotted boxes, select-all box included.
table.addEventListener('mdSortChange', (e) => {
const { column, order } = e.detail; // MdTableSortState
table.animateNextChange(); // before the DOM mutation
render(sortRows(data, column, order));// YOU sort
});
table.addEventListener('mdSelectionChange', (e) => {
// MdTableSelectionState: count, total, values, all, indeterminate
console.log(e.detail.count, e.detail.values);
});
</script>import { useMemo, useRef, useState } from 'react';
import {
MdCheckbox, MdTable, MdTableBody, MdTableCell, MdTableContainer,
MdTableHead, MdTableRow, MdTableSortLabel,
} from '@awc-ui/react';
const INVOICES = [
{ id: 'inv-1', client: 'Acme Corp', amount: 1200 },
{ id: 'inv-2', client: 'Globex', amount: 3120 },
{ id: 'inv-3', client: 'Initech', amount: 860 },
];
const money = (n) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
export function InvoiceEvents() {
const tableRef = useRef(null);
const [sort, setSort] = useState({ column: '', order: 'none' });
const [log, setLog] = useState([]);
const push = (line) => setLog((l) => [line, ...l].slice(0, 6));
// The table reports sort state; the rows are yours to order.
const rows = useMemo(() => {
if (!sort.column || sort.order === 'none') return INVOICES;
const dir = sort.order === 'desc' ? -1 : 1;
return [...INVOICES].sort((a, b) => {
const av = a[sort.column];
const bv = b[sort.column];
return (typeof av === 'number' ? av - bv : String(av).localeCompare(String(bv))) * dir;
});
}, [sort]);
return (
<>
<MdTableContainer variant="outlined">
<MdTable
ref={tableRef}
columnTemplate="auto 2fr 1fr"
selection="multiple"
label="Invoices"
onMdSortChange={(e) => {
tableRef.current?.animateNextChange();
setSort(e.detail);
push(`mdSortChange column=${e.detail.column || '(none)'} order=${e.detail.order}`);
}}
onMdSelectionChange={(e) =>
push(`mdSelectionChange count=${e.detail.count}/${e.detail.total} values=[${e.detail.values.join(', ')}]`)
}
>
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head padding="checkbox">
<MdCheckbox aria-label="Select all rows" onMdChange={() => tableRef.current?.toggleSelectAll()} />
</MdTableCell>
<MdTableCell head scope="col"><MdTableSortLabel column="client">Client</MdTableSortLabel></MdTableCell>
<MdTableCell head scope="col" numeric><MdTableSortLabel column="amount">Amount</MdTableSortLabel></MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
{rows.map((row) => (
<MdTableRow key={row.id} value={row.id}>
<MdTableCell padding="checkbox"><MdCheckbox aria-label={`Select ${row.client}`} /></MdTableCell>
<MdTableCell head scope="row">{row.client}</MdTableCell>
<MdTableCell numeric>{money(row.amount)}</MdTableCell>
</MdTableRow>
))}
</MdTableBody>
</MdTable>
</MdTableContainer>
<pre role="status">{log.join('\n')}</pre>
</>
);
}// invoice-events.component.ts
import { Component, CUSTOM_ELEMENTS_SCHEMA, ElementRef, ViewChild } from '@angular/core';
import { CommonModule } from '@angular/common';
import { defineCustomElements } from '@awc-ui/core/loader';
defineCustomElements(window);
interface Invoice { id: string; client: string; amount: number; }
@Component({
selector: 'app-invoice-events',
standalone: true,
imports: [CommonModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `
<md-table-container variant="outlined">
<md-table
#table
column-template="auto 2fr 1fr"
selection="multiple"
label="Invoices"
(mdSortChange)="onSort($event)"
(mdSelectionChange)="onSelection($event)"
>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox">
<md-checkbox aria-label="Select all rows" (mdChange)="selectAll()"></md-checkbox>
</md-table-cell>
<md-table-cell head scope="col"><md-table-sort-label column="client">Client</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col" numeric><md-table-sort-label column="amount">Amount</md-table-sort-label></md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row *ngFor="let row of rows; trackBy: trackById" [attr.value]="row.id">
<md-table-cell padding="checkbox">
<md-checkbox [attr.aria-label]="'Select ' + row.client"></md-checkbox>
</md-table-cell>
<md-table-cell head scope="row">{{ row.client }}</md-table-cell>
<md-table-cell numeric>{{ row.amount | currency }}</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<pre role="status">{{ log.join(' ') }}</pre>
`,
})
export class InvoiceEventsComponent {
@ViewChild('table') tableRef!: ElementRef<HTMLElement & {
toggleSelectAll(): Promise<void>;
animateNextChange(): Promise<void>;
}>;
private readonly invoices: Invoice[] = [
{ id: 'inv-1', client: 'Acme Corp', amount: 1200 },
{ id: 'inv-2', client: 'Globex', amount: 3120 },
{ id: 'inv-3', client: 'Initech', amount: 860 },
];
rows = this.invoices;
log: string[] = [];
selectAll() { void this.tableRef.nativeElement.toggleSelectAll(); }
onSort(event: Event) {
const { column, order } = (event as CustomEvent<{ column: string; order: string }>).detail;
this.push(`mdSortChange column=${column || '(none)'} order=${order}`);
void this.tableRef.nativeElement.animateNextChange();
if (!column || order === 'none') { this.rows = this.invoices; return; }
const dir = order === 'desc' ? -1 : 1;
this.rows = [...this.invoices].sort((a, b) => {
const av = a[column as keyof Invoice];
const bv = b[column as keyof Invoice];
return (typeof av === 'number' && typeof bv === 'number'
? av - bv
: String(av).localeCompare(String(bv))) * dir;
});
}
onSelection(event: Event) {
const { count, total, values } = (event as CustomEvent<{ count: number; total: number; values: string[] }>).detail;
this.push(`mdSelectionChange count=${count}/${total} values=[${values.join(', ')}]`);
}
private push(line: string) { this.log = [line, ...this.log].slice(0, 6); }
trackById = (_: number, row: Invoice) => row.id;
}<script setup lang="ts">
import { computed, ref } from 'vue';
import '@awc-ui/core/define';
const INVOICES = [
{ id: 'inv-1', client: 'Acme Corp', amount: 1200 },
{ id: 'inv-2', client: 'Globex', amount: 3120 },
{ id: 'inv-3', client: 'Initech', amount: 860 },
];
const money = (n: number) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
const table = ref<any>(null);
const sort = ref({ column: '', order: 'none' });
const log = ref<string[]>([]);
const push = (line: string) => { log.value = [line, ...log.value].slice(0, 6); };
const rows = computed(() => {
if (!sort.value.column || sort.value.order === 'none') return INVOICES;
const dir = sort.value.order === 'desc' ? -1 : 1;
return [...INVOICES].sort((a, b) => {
const av = a[sort.value.column];
const bv = b[sort.value.column];
return (typeof av === 'number' ? av - bv : String(av).localeCompare(String(bv))) * dir;
});
});
function onSort(e: CustomEvent) {
push(`mdSortChange column=${e.detail.column || '(none)'} order=${e.detail.order}`);
table.value?.animateNextChange();
sort.value = e.detail;
}
function onSelection(e: CustomEvent) {
push(`mdSelectionChange count=${e.detail.count}/${e.detail.total} values=[${e.detail.values.join(', ')}]`);
}
</script>
<template>
<md-table-container variant="outlined">
<md-table
ref="table"
column-template="auto 2fr 1fr"
selection="multiple"
label="Invoices"
@mdSortChange="onSort"
@mdSelectionChange="onSelection"
>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox">
<md-checkbox aria-label="Select all rows" @mdChange="table.toggleSelectAll()"></md-checkbox>
</md-table-cell>
<md-table-cell head scope="col"><md-table-sort-label column="client">Client</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col" numeric><md-table-sort-label column="amount">Amount</md-table-sort-label></md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row v-for="row in rows" :key="row.id" :value="row.id">
<md-table-cell padding="checkbox">
<md-checkbox :aria-label="'Select ' + row.client"></md-checkbox>
</md-table-cell>
<md-table-cell head scope="row">{{ row.client }}</md-table-cell>
<md-table-cell numeric>{{ money(row.amount) }}</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<pre role="status">{{ log.join('\n') }}</pre>
</template><script lang="ts">
import '@awc-ui/core/define';
const INVOICES = [
{ id: 'inv-1', client: 'Acme Corp', amount: 1200 },
{ id: 'inv-2', client: 'Globex', amount: 3120 },
{ id: 'inv-3', client: 'Initech', amount: 860 },
];
const money = (n) => n.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
let table;
let sort = { column: '', order: 'none' };
let log = [];
const push = (line) => (log = [line, ...log].slice(0, 6));
$: rows = !sort.column || sort.order === 'none'
? INVOICES
: [...INVOICES].sort((a, b) => {
const dir = sort.order === 'desc' ? -1 : 1;
const av = a[sort.column];
const bv = b[sort.column];
return (typeof av === 'number' ? av - bv : String(av).localeCompare(String(bv))) * dir;
});
function onSort(e) {
push(`mdSortChange column=${e.detail.column || '(none)'} order=${e.detail.order}`);
table.animateNextChange();
sort = e.detail;
}
function onSelection(e) {
push(`mdSelectionChange count=${e.detail.count}/${e.detail.total} values=[${e.detail.values.join(', ')}]`);
}
</script>
<md-table-container variant="outlined">
<md-table
bind:this={table}
column-template="auto 2fr 1fr"
selection="multiple"
label="Invoices"
on:mdSortChange={onSort}
on:mdSelectionChange={onSelection}
>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox">
<md-checkbox aria-label="Select all rows" on:mdChange={() => table.toggleSelectAll()}></md-checkbox>
</md-table-cell>
<md-table-cell head scope="col"><md-table-sort-label column="client">Client</md-table-sort-label></md-table-cell>
<md-table-cell head scope="col" numeric><md-table-sort-label column="amount">Amount</md-table-sort-label></md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
{#each rows as row (row.id)}
<md-table-row value={row.id}>
<md-table-cell padding="checkbox">
<md-checkbox aria-label={'Select ' + row.client}></md-checkbox>
</md-table-cell>
<md-table-cell head scope="row">{row.client}</md-table-cell>
<md-table-cell numeric>{money(row.amount)}</md-table-cell>
</md-table-row>
{/each}
</md-table-body>
</md-table>
</md-table-container>
<pre role="status">{log.join('\n')}</pre>| Property | Attribute | Type | Default | Reflects |
|---|---|---|---|---|
density | density | | 'compact' | 'standard' | 'comfortable' | 0 | -1 | -2 | -3 | -4 | 'standard' | Yes |
columnTemplate | column-template | string | '' | — |
columns | columns | number | 0 | — |
minWidth | min-width | string | '' | — |
stickyHeader | sticky-header | boolean | false | Yes |
frozenHeader | frozen-header | boolean | false | Yes |
keepHeight | keep-height | boolean | true | Yes |
scrollbar | scrollbar | 'overlay' | 'gutter' | 'overlay' | Yes |
pinMode | pin-mode | 'stack' | 'static' | 'stack' | Yes |
pinIcon | pin-icon | string | '' | — |
stickyFooter | sticky-footer | boolean | false | Yes |
striped | striped | boolean | false | Yes |
noDividers | no-dividers | boolean | false | Yes |
hoverable | hoverable | boolean | true | Yes |
motion | motion | 'expressive' | 'none' | 'expressive' | Yes |
selection | selection | 'none' | 'single' | 'multiple' | 'none' | Yes |
sortBy | sort-by | string | '' | Yes |
sortOrder | sort-order | MdTableSortOrder | 'asc' | Yes |
loading | loading | boolean | false | Yes |
loadingMode | loading-mode | 'overlay' | 'skeleton' | 'overlay' | Yes |
loadingRows | loading-rows | number | 4 | — |
empty | empty | boolean | false | Yes |
label | label | string | '' | — |
caption | caption | string | '' | — |
summary | summary | string | '' | — |
rowOffset | row-offset | number | 0 | — |
rowCount | row-count | number | 0 | — |
| Method | Parameters |
|---|---|
getSelection() | none |
selectAll() | none |
deselectAll() | none |
toggleSelectAll() | none |
setSort() | column: string, order: MdTableSortOrder |
pinColumn() | column: number, side: 'start' | 'end' | 'none' |
setColumnVisibility() | column: number, visible: boolean |
animateNextChange() | none |
| Slot | Description |
|---|---|
(default) | — |
empty | — |
head | — |
loader | — |
loading | — |
Override on the host element for per-instance theming:
| Property | Description |
|---|---|
--md-table-row-height-compact | 36px (dense) |
--md-table-row-height-standard | 52px |
--md-table-row-height-comfortable | 60px |
--md-table-header-height | 56px |
--md-table-cell-padding-inline | per-density default |
--md-table-divider-color | between rows |
--md-table-stripe-color | every-other-row overlay color |
--md-table-head-bg | background for thead rows |
--md-table-head-color | text color for thead |
--md-table-foot-bg | background for tfoot rows |
--md-table-row-hover-color | row hover state-layer color |
--md-table-row-selected-bg | selected row background |
--md-table-row-selected-color | selected row text |
--md-table-columns-template | grid-template-columns override |
--md-table-cell-padding-block | — |
--md-table-min-width | — |
--md-table-body-max-h | — |
--md-table-empty-min-height | — |
Style internal elements through shadow DOM with ::part():
| Part | Description |
|---|---|
skeleton-row | — |
empty | The empty-state region (when `empty`) |
caption | The optional caption above the head |
header-grid | — |
grid | The grid container holding all rows |
hscrollbar | — |
hscrollbar-thumb | — |
vscrollbar | — |
vscrollbar-thumb | — |
loading | The loading overlay (when `loading`) |
progress | — |
<table>, the ARIA
structure carries the semantics: the rowgroups are rowgroups,
md-table-row is a row, and md-table-cell is a cell — or a column / row
header via head + scope.scope right. head alone styles a cell like a header; scope="col"
/ scope="row" / scope="colgroup" is what makes data cells announce with
their column or row. The trio — rowgroup on the row, head and scope on
the cell — works together.label names the role="table". When it’s omitted the caption text names
the table instead (IDREFs can’t cross the shadow boundary, so the caption text
is mirrored into aria-label rather than referenced); the generic “Data
table” is the last resort. Never ship an unnamed data grid.summary describes a complex table’s structure (“Columns are grouped under
Profile and Employment”) and is exposed as aria-description.row-offset (0-based index of the first rendered
row) and row-count (the full dataset size), so each row gets an
aria-rowindex and AT announces “row 42 of 5000” instead of a position within
the current page. Update row-offset on every page change. row-offset only
applies when the DOM holds just the current page — the table counts every
md-table-row present, visible or not, so if you page by hiding rows rather
than removing them the indices are already absolute and row-offset would
double-count them. (Setting row-offset alone doesn’t re-stamp anything
either: change it together with the rows.)motion="none" for reduced-motion users; prefers-reduced-motion is honoured
automatically.Everything focusable in a table is something you slotted into a cell, so the tab
order is the order you authored. Tab through this one: each row’s expand toggle
and checkbox is a stop, and the rows announce their position in the whole
dataset because row-offset and row-count are set:
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined">
<md-table
column-template="auto auto 2fr 1fr"
selection="multiple"
label="Invoices"
caption="Invoices — rows 21 to 23 of 120"
summary="Four columns: an expand toggle, a selection checkbox, the client name and the amount in US dollars."
row-offset="20"
row-count="120"
keep-height="false"
>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox"></md-table-cell>
<md-table-cell head padding="checkbox"></md-table-cell>
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="a1" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Acme Corp"></md-table-expand-toggle></md-table-cell>
<md-table-cell padding="checkbox"><md-checkbox aria-label="Select Acme Corp"></md-checkbox></md-table-cell>
<md-table-cell head scope="row">Acme Corp</md-table-cell>
<md-table-cell numeric>$1,200.00</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Three line items · paid by card ending 4242 on 4 May.</div>
</md-table-row>
<md-table-row value="a2" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Globex"></md-table-expand-toggle></md-table-cell>
<md-table-cell padding="checkbox"><md-checkbox aria-label="Select Globex"></md-checkbox></md-table-cell>
<md-table-cell head scope="row">Globex</md-table-cell>
<md-table-cell numeric>$3,120.00</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Awaiting PO approval — chased 2 days ago.</div>
</md-table-row>
<md-table-row value="a3" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Initech"></md-table-expand-toggle></md-table-cell>
<md-table-cell padding="checkbox"><md-checkbox aria-label="Select Initech"></md-checkbox></md-table-cell>
<md-table-cell head scope="row">Initech</md-table-cell>
<md-table-cell numeric>$860.00</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Partially credited against invoice 41.</div>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>import { MdCheckbox, MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableExpandToggle, MdTableHead, MdTableRow } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdTableContainer variant="outlined">
<MdTable
columnTemplate="auto auto 2fr 1fr"
selection="multiple"
label="Invoices"
caption="Invoices — rows 21 to 23 of 120"
summary="Four columns: an expand toggle, a selection checkbox, the client name and the amount in US dollars."
rowOffset="20"
rowCount="120"
keepHeight="false"
>
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head padding="checkbox"></MdTableCell>
<MdTableCell head padding="checkbox"></MdTableCell>
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="a1" expandable>
<MdTableCell padding="checkbox"><MdTableExpandToggle buttonLabel="Expand details for Acme Corp"></MdTableExpandToggle></MdTableCell>
<MdTableCell padding="checkbox"><MdCheckbox aria-label="Select Acme Corp"></MdCheckbox></MdTableCell>
<MdTableCell head scope="row">Acme Corp</MdTableCell>
<MdTableCell numeric>$1,200.00</MdTableCell>
<div slot="expanded" style={{ padding: '12px 16px', color: 'var(--md-sys-color-on-surface-variant)' }}>Three line items · paid by card ending 4242 on 4 May.</div>
</MdTableRow>
<MdTableRow value="a2" expandable>
<MdTableCell padding="checkbox"><MdTableExpandToggle buttonLabel="Expand details for Globex"></MdTableExpandToggle></MdTableCell>
<MdTableCell padding="checkbox"><MdCheckbox aria-label="Select Globex"></MdCheckbox></MdTableCell>
<MdTableCell head scope="row">Globex</MdTableCell>
<MdTableCell numeric>$3,120.00</MdTableCell>
<div slot="expanded" style={{ padding: '12px 16px', color: 'var(--md-sys-color-on-surface-variant)' }}>Awaiting PO approval — chased 2 days ago.</div>
</MdTableRow>
<MdTableRow value="a3" expandable>
<MdTableCell padding="checkbox"><MdTableExpandToggle buttonLabel="Expand details for Initech"></MdTableExpandToggle></MdTableCell>
<MdTableCell padding="checkbox"><MdCheckbox aria-label="Select Initech"></MdCheckbox></MdTableCell>
<MdTableCell head scope="row">Initech</MdTableCell>
<MdTableCell numeric>$860.00</MdTableCell>
<div slot="expanded" style={{ padding: '12px 16px', color: 'var(--md-sys-color-on-surface-variant)' }}>Partially credited against invoice 41.</div>
</MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-table-container variant="outlined">
<md-table
column-template="auto auto 2fr 1fr"
selection="multiple"
label="Invoices"
caption="Invoices — rows 21 to 23 of 120"
summary="Four columns: an expand toggle, a selection checkbox, the client name and the amount in US dollars."
row-offset="20"
row-count="120"
keep-height="false"
>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox"></md-table-cell>
<md-table-cell head padding="checkbox"></md-table-cell>
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="a1" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Acme Corp"></md-table-expand-toggle></md-table-cell>
<md-table-cell padding="checkbox"><md-checkbox aria-label="Select Acme Corp"></md-checkbox></md-table-cell>
<md-table-cell head scope="row">Acme Corp</md-table-cell>
<md-table-cell numeric>$1,200.00</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Three line items · paid by card ending 4242 on 4 May.</div>
</md-table-row>
<md-table-row value="a2" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Globex"></md-table-expand-toggle></md-table-cell>
<md-table-cell padding="checkbox"><md-checkbox aria-label="Select Globex"></md-checkbox></md-table-cell>
<md-table-cell head scope="row">Globex</md-table-cell>
<md-table-cell numeric>$3,120.00</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Awaiting PO approval — chased 2 days ago.</div>
</md-table-row>
<md-table-row value="a3" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Initech"></md-table-expand-toggle></md-table-cell>
<md-table-cell padding="checkbox"><md-checkbox aria-label="Select Initech"></md-checkbox></md-table-cell>
<md-table-cell head scope="row">Initech</md-table-cell>
<md-table-cell numeric>$860.00</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Partially credited against invoice 41.</div>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-table-container variant="outlined">
<md-table
column-template="auto auto 2fr 1fr"
selection="multiple"
label="Invoices"
caption="Invoices — rows 21 to 23 of 120"
summary="Four columns: an expand toggle, a selection checkbox, the client name and the amount in US dollars."
row-offset="20"
row-count="120"
keep-height="false"
>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox"></md-table-cell>
<md-table-cell head padding="checkbox"></md-table-cell>
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="a1" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Acme Corp"></md-table-expand-toggle></md-table-cell>
<md-table-cell padding="checkbox"><md-checkbox aria-label="Select Acme Corp"></md-checkbox></md-table-cell>
<md-table-cell head scope="row">Acme Corp</md-table-cell>
<md-table-cell numeric>$1,200.00</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Three line items · paid by card ending 4242 on 4 May.</div>
</md-table-row>
<md-table-row value="a2" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Globex"></md-table-expand-toggle></md-table-cell>
<md-table-cell padding="checkbox"><md-checkbox aria-label="Select Globex"></md-checkbox></md-table-cell>
<md-table-cell head scope="row">Globex</md-table-cell>
<md-table-cell numeric>$3,120.00</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Awaiting PO approval — chased 2 days ago.</div>
</md-table-row>
<md-table-row value="a3" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Initech"></md-table-expand-toggle></md-table-cell>
<md-table-cell padding="checkbox"><md-checkbox aria-label="Select Initech"></md-checkbox></md-table-cell>
<md-table-cell head scope="row">Initech</md-table-cell>
<md-table-cell numeric>$860.00</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Partially credited against invoice 41.</div>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</template><script>
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined">
<md-table
column-template="auto auto 2fr 1fr"
selection="multiple"
label="Invoices"
caption="Invoices — rows 21 to 23 of 120"
summary="Four columns: an expand toggle, a selection checkbox, the client name and the amount in US dollars."
row-offset="20"
row-count="120"
keep-height="false"
>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox"></md-table-cell>
<md-table-cell head padding="checkbox"></md-table-cell>
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="a1" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Acme Corp"></md-table-expand-toggle></md-table-cell>
<md-table-cell padding="checkbox"><md-checkbox aria-label="Select Acme Corp"></md-checkbox></md-table-cell>
<md-table-cell head scope="row">Acme Corp</md-table-cell>
<md-table-cell numeric>$1,200.00</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Three line items · paid by card ending 4242 on 4 May.</div>
</md-table-row>
<md-table-row value="a2" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Globex"></md-table-expand-toggle></md-table-cell>
<md-table-cell padding="checkbox"><md-checkbox aria-label="Select Globex"></md-checkbox></md-table-cell>
<md-table-cell head scope="row">Globex</md-table-cell>
<md-table-cell numeric>$3,120.00</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Awaiting PO approval — chased 2 days ago.</div>
</md-table-row>
<md-table-row value="a3" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Initech"></md-table-expand-toggle></md-table-cell>
<md-table-cell padding="checkbox"><md-checkbox aria-label="Select Initech"></md-checkbox></md-table-cell>
<md-table-cell head scope="row">Initech</md-table-cell>
<md-table-cell numeric>$860.00</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Partially credited against invoice 41.</div>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>RTL — column order, pinning sides and the scrollbars mirror under
dir="rtl". column-template is a raw grid value though, so track sizes
follow the columns rather than adapting to them: a width hand-tuned for one
language needs re-checking in the next. See the
RTL story and RTL.
The same table in both directions. Nothing is re-authored between them —
only dir changes. The tracks are laid out in the inline direction, so the
first column moves to the right-hand edge and the horizontal scrollbar starts
from the other end:
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div style="display:grid;grid-template-columns:auto 1fr;gap:14px 16px;align-items:center;">
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">ltr</span>
<div dir="ltr">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="الفواتير">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">العميل</md-table-cell>
<md-table-cell head scope="col" numeric>المبلغ</md-table-cell>
<md-table-cell head scope="col">الحالة</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="1"><md-table-cell head scope="row">أكمي</md-table-cell><md-table-cell numeric>١٢٠٠٫٠٠</md-table-cell><md-table-cell>مدفوع</md-table-cell></md-table-row>
<md-table-row value="2"><md-table-cell head scope="row">غلوبكس</md-table-cell><md-table-cell numeric>٣١٢٠٫٠٠</md-table-cell><md-table-cell>معلق</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">rtl</span>
<div dir="rtl">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="الفواتير">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">العميل</md-table-cell>
<md-table-cell head scope="col" numeric>المبلغ</md-table-cell>
<md-table-cell head scope="col">الحالة</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="1"><md-table-cell head scope="row">أكمي</md-table-cell><md-table-cell numeric>١٢٠٠٫٠٠</md-table-cell><md-table-cell>مدفوع</md-table-cell></md-table-row>
<md-table-row value="2"><md-table-cell head scope="row">غلوبكس</md-table-cell><md-table-cell numeric>٣١٢٠٫٠٠</md-table-cell><md-table-cell>معلق</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
</div>import { MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead, MdTableRow } from '@awc-ui/react';
export function Demo() {
return (
<>
<div style={{ display: 'grid', gridTemplateColumns: 'auto 1fr', gap: '14px 16px', alignItems: 'center' }}>
<span style={{ inlineSize: '3.5rem', opacity: '.65', fontSize: '.75rem', fontFamily: 'ui-monospace,monospace' }}>ltr</span>
<div dir="ltr">
<MdTableContainer variant="outlined">
<MdTable columnTemplate="2fr 1fr 1fr" label="الفواتير">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">العميل</MdTableCell>
<MdTableCell head scope="col" numeric>المبلغ</MdTableCell>
<MdTableCell head scope="col">الحالة</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="1"><MdTableCell head scope="row">أكمي</MdTableCell><MdTableCell numeric>١٢٠٠٫٠٠</MdTableCell><MdTableCell>مدفوع</MdTableCell></MdTableRow>
<MdTableRow value="2"><MdTableCell head scope="row">غلوبكس</MdTableCell><MdTableCell numeric>٣١٢٠٫٠٠</MdTableCell><MdTableCell>معلق</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
</div>
<span style={{ inlineSize: '3.5rem', opacity: '.65', fontSize: '.75rem', fontFamily: 'ui-monospace,monospace' }}>rtl</span>
<div dir="rtl">
<MdTableContainer variant="outlined">
<MdTable columnTemplate="2fr 1fr 1fr" label="الفواتير">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">العميل</MdTableCell>
<MdTableCell head scope="col" numeric>المبلغ</MdTableCell>
<MdTableCell head scope="col">الحالة</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="1"><MdTableCell head scope="row">أكمي</MdTableCell><MdTableCell numeric>١٢٠٠٫٠٠</MdTableCell><MdTableCell>مدفوع</MdTableCell></MdTableRow>
<MdTableRow value="2"><MdTableCell head scope="row">غلوبكس</MdTableCell><MdTableCell numeric>٣١٢٠٫٠٠</MdTableCell><MdTableCell>معلق</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
</div>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div style="display:grid;grid-template-columns:auto 1fr;gap:14px 16px;align-items:center;">
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">ltr</span>
<div dir="ltr">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="الفواتير">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">العميل</md-table-cell>
<md-table-cell head scope="col" numeric>المبلغ</md-table-cell>
<md-table-cell head scope="col">الحالة</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="1"><md-table-cell head scope="row">أكمي</md-table-cell><md-table-cell numeric>١٢٠٠٫٠٠</md-table-cell><md-table-cell>مدفوع</md-table-cell></md-table-row>
<md-table-row value="2"><md-table-cell head scope="row">غلوبكس</md-table-cell><md-table-cell numeric>٣١٢٠٫٠٠</md-table-cell><md-table-cell>معلق</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">rtl</span>
<div dir="rtl">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="الفواتير">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">العميل</md-table-cell>
<md-table-cell head scope="col" numeric>المبلغ</md-table-cell>
<md-table-cell head scope="col">الحالة</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="1"><md-table-cell head scope="row">أكمي</md-table-cell><md-table-cell numeric>١٢٠٠٫٠٠</md-table-cell><md-table-cell>مدفوع</md-table-cell></md-table-row>
<md-table-row value="2"><md-table-cell head scope="row">غلوبكس</md-table-cell><md-table-cell numeric>٣١٢٠٫٠٠</md-table-cell><md-table-cell>معلق</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
</div><script setup>
import '@awc-ui/core/define';
</script>
<template>
<div style="display:grid;grid-template-columns:auto 1fr;gap:14px 16px;align-items:center;">
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">ltr</span>
<div dir="ltr">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="الفواتير">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">العميل</md-table-cell>
<md-table-cell head scope="col" numeric>المبلغ</md-table-cell>
<md-table-cell head scope="col">الحالة</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="1"><md-table-cell head scope="row">أكمي</md-table-cell><md-table-cell numeric>١٢٠٠٫٠٠</md-table-cell><md-table-cell>مدفوع</md-table-cell></md-table-row>
<md-table-row value="2"><md-table-cell head scope="row">غلوبكس</md-table-cell><md-table-cell numeric>٣١٢٠٫٠٠</md-table-cell><md-table-cell>معلق</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">rtl</span>
<div dir="rtl">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="الفواتير">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">العميل</md-table-cell>
<md-table-cell head scope="col" numeric>المبلغ</md-table-cell>
<md-table-cell head scope="col">الحالة</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="1"><md-table-cell head scope="row">أكمي</md-table-cell><md-table-cell numeric>١٢٠٠٫٠٠</md-table-cell><md-table-cell>مدفوع</md-table-cell></md-table-row>
<md-table-row value="2"><md-table-cell head scope="row">غلوبكس</md-table-cell><md-table-cell numeric>٣١٢٠٫٠٠</md-table-cell><md-table-cell>معلق</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
</div>
</template><script>
import '@awc-ui/core/define';
</script>
<div style="display:grid;grid-template-columns:auto 1fr;gap:14px 16px;align-items:center;">
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">ltr</span>
<div dir="ltr">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="الفواتير">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">العميل</md-table-cell>
<md-table-cell head scope="col" numeric>المبلغ</md-table-cell>
<md-table-cell head scope="col">الحالة</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="1"><md-table-cell head scope="row">أكمي</md-table-cell><md-table-cell numeric>١٢٠٠٫٠٠</md-table-cell><md-table-cell>مدفوع</md-table-cell></md-table-row>
<md-table-row value="2"><md-table-cell head scope="row">غلوبكس</md-table-cell><md-table-cell numeric>٣١٢٠٫٠٠</md-table-cell><md-table-cell>معلق</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">rtl</span>
<div dir="rtl">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="الفواتير">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">العميل</md-table-cell>
<md-table-cell head scope="col" numeric>المبلغ</md-table-cell>
<md-table-cell head scope="col">الحالة</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="1"><md-table-cell head scope="row">أكمي</md-table-cell><md-table-cell numeric>١٢٠٠٫٠٠</md-table-cell><md-table-cell>مدفوع</md-table-cell></md-table-row>
<md-table-row value="2"><md-table-cell head scope="row">غلوبكس</md-table-cell><md-table-cell numeric>٣١٢٠٫٠٠</md-table-cell><md-table-cell>معلق</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
</div>column-template is a raw grid value, so size it in frThe direction mirrors for free; the track sizes do not adapt. A width hand-tuned
for one language’s headers is still that width after translation, and a px
track that fitted “Client” will crowd or truncate the same column in Arabic or
German. Size the text columns in fr / minmax() and keep px for genuinely
fixed content — a checkbox, an icon column:
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div style="display:grid;grid-template-columns:auto 1fr;gap:14px 16px;align-items:center;">
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">correct</span>
<div dir="rtl">
<md-table-container variant="outlined">
<md-table column-template="minmax(120px, 2fr) 1fr 1fr" label="أرقام المبيعات">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">المنطقة الجغرافية</md-table-cell>
<md-table-cell head scope="col" numeric>المبلغ</md-table-cell>
<md-table-cell head scope="col">الحالة</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="c1"><md-table-cell head scope="row">الشرق الأوسط</md-table-cell><md-table-cell numeric>١٢٠٠٫٠٠</md-table-cell><md-table-cell>مدفوع</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">wrong</span>
<div dir="rtl">
<md-table-container variant="outlined">
<md-table column-template="72px 1fr 1fr" label="أرقام المبيعات">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">المنطقة الجغرافية</md-table-cell>
<md-table-cell head scope="col" numeric>المبلغ</md-table-cell>
<md-table-cell head scope="col">الحالة</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="w1"><md-table-cell head scope="row">الشرق الأوسط</md-table-cell><md-table-cell numeric>١٢٠٠٫٠٠</md-table-cell><md-table-cell>مدفوع</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
</div>import { MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead, MdTableRow } from '@awc-ui/react';
export function Demo() {
return (
<>
<div style={{ display: 'grid', gridTemplateColumns: 'auto 1fr', gap: '14px 16px', alignItems: 'center' }}>
<span style={{ inlineSize: '3.5rem', opacity: '.65', fontSize: '.75rem', fontFamily: 'ui-monospace,monospace' }}>correct</span>
<div dir="rtl">
<MdTableContainer variant="outlined">
<MdTable columnTemplate="minmax(120px, 2fr) 1fr 1fr" label="أرقام المبيعات">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">المنطقة الجغرافية</MdTableCell>
<MdTableCell head scope="col" numeric>المبلغ</MdTableCell>
<MdTableCell head scope="col">الحالة</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="c1"><MdTableCell head scope="row">الشرق الأوسط</MdTableCell><MdTableCell numeric>١٢٠٠٫٠٠</MdTableCell><MdTableCell>مدفوع</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
</div>
<span style={{ inlineSize: '3.5rem', opacity: '.65', fontSize: '.75rem', fontFamily: 'ui-monospace,monospace' }}>wrong</span>
<div dir="rtl">
<MdTableContainer variant="outlined">
<MdTable columnTemplate="72px 1fr 1fr" label="أرقام المبيعات">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">المنطقة الجغرافية</MdTableCell>
<MdTableCell head scope="col" numeric>المبلغ</MdTableCell>
<MdTableCell head scope="col">الحالة</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="w1"><MdTableCell head scope="row">الشرق الأوسط</MdTableCell><MdTableCell numeric>١٢٠٠٫٠٠</MdTableCell><MdTableCell>مدفوع</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
</div>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div style="display:grid;grid-template-columns:auto 1fr;gap:14px 16px;align-items:center;">
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">correct</span>
<div dir="rtl">
<md-table-container variant="outlined">
<md-table column-template="minmax(120px, 2fr) 1fr 1fr" label="أرقام المبيعات">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">المنطقة الجغرافية</md-table-cell>
<md-table-cell head scope="col" numeric>المبلغ</md-table-cell>
<md-table-cell head scope="col">الحالة</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="c1"><md-table-cell head scope="row">الشرق الأوسط</md-table-cell><md-table-cell numeric>١٢٠٠٫٠٠</md-table-cell><md-table-cell>مدفوع</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">wrong</span>
<div dir="rtl">
<md-table-container variant="outlined">
<md-table column-template="72px 1fr 1fr" label="أرقام المبيعات">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">المنطقة الجغرافية</md-table-cell>
<md-table-cell head scope="col" numeric>المبلغ</md-table-cell>
<md-table-cell head scope="col">الحالة</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="w1"><md-table-cell head scope="row">الشرق الأوسط</md-table-cell><md-table-cell numeric>١٢٠٠٫٠٠</md-table-cell><md-table-cell>مدفوع</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
</div><script setup>
import '@awc-ui/core/define';
</script>
<template>
<div style="display:grid;grid-template-columns:auto 1fr;gap:14px 16px;align-items:center;">
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">correct</span>
<div dir="rtl">
<md-table-container variant="outlined">
<md-table column-template="minmax(120px, 2fr) 1fr 1fr" label="أرقام المبيعات">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">المنطقة الجغرافية</md-table-cell>
<md-table-cell head scope="col" numeric>المبلغ</md-table-cell>
<md-table-cell head scope="col">الحالة</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="c1"><md-table-cell head scope="row">الشرق الأوسط</md-table-cell><md-table-cell numeric>١٢٠٠٫٠٠</md-table-cell><md-table-cell>مدفوع</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">wrong</span>
<div dir="rtl">
<md-table-container variant="outlined">
<md-table column-template="72px 1fr 1fr" label="أرقام المبيعات">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">المنطقة الجغرافية</md-table-cell>
<md-table-cell head scope="col" numeric>المبلغ</md-table-cell>
<md-table-cell head scope="col">الحالة</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="w1"><md-table-cell head scope="row">الشرق الأوسط</md-table-cell><md-table-cell numeric>١٢٠٠٫٠٠</md-table-cell><md-table-cell>مدفوع</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
</div>
</template><script>
import '@awc-ui/core/define';
</script>
<div style="display:grid;grid-template-columns:auto 1fr;gap:14px 16px;align-items:center;">
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">correct</span>
<div dir="rtl">
<md-table-container variant="outlined">
<md-table column-template="minmax(120px, 2fr) 1fr 1fr" label="أرقام المبيعات">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">المنطقة الجغرافية</md-table-cell>
<md-table-cell head scope="col" numeric>المبلغ</md-table-cell>
<md-table-cell head scope="col">الحالة</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="c1"><md-table-cell head scope="row">الشرق الأوسط</md-table-cell><md-table-cell numeric>١٢٠٠٫٠٠</md-table-cell><md-table-cell>مدفوع</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">wrong</span>
<div dir="rtl">
<md-table-container variant="outlined">
<md-table column-template="72px 1fr 1fr" label="أرقام المبيعات">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">المنطقة الجغرافية</md-table-cell>
<md-table-cell head scope="col" numeric>المبلغ</md-table-cell>
<md-table-cell head scope="col">الحالة</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="w1"><md-table-cell head scope="row">الشرق الأوسط</md-table-cell><md-table-cell numeric>١٢٠٠٫٠٠</md-table-cell><md-table-cell>مدفوع</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
</div>density takes the numeric rungs as well as the semantic words, and a numeric
value locally overrides the inherited data-density. All five rungs, the same
two rows, so the taper is visible in one place:
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div style="display:grid;grid-template-columns:auto 1fr;gap:14px 16px;align-items:center;">
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">0</span>
<md-table-container variant="outlined"><md-table column-template="2fr 1fr" density="0" label="Rung 0"><md-table-head><md-table-row rowgroup="head"><md-table-cell head scope="col">Client</md-table-cell><md-table-cell head scope="col" numeric>Amount</md-table-cell></md-table-row></md-table-head><md-table-body><md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row><md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row></md-table-body></md-table></md-table-container>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-1</span>
<md-table-container variant="outlined"><md-table column-template="2fr 1fr" density="-1" label="Rung -1"><md-table-head><md-table-row rowgroup="head"><md-table-cell head scope="col">Client</md-table-cell><md-table-cell head scope="col" numeric>Amount</md-table-cell></md-table-row></md-table-head><md-table-body><md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row><md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row></md-table-body></md-table></md-table-container>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-2</span>
<md-table-container variant="outlined"><md-table column-template="2fr 1fr" density="-2" label="Rung -2"><md-table-head><md-table-row rowgroup="head"><md-table-cell head scope="col">Client</md-table-cell><md-table-cell head scope="col" numeric>Amount</md-table-cell></md-table-row></md-table-head><md-table-body><md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row><md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row></md-table-body></md-table></md-table-container>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-3</span>
<md-table-container variant="outlined"><md-table column-template="2fr 1fr" density="-3" label="Rung -3"><md-table-head><md-table-row rowgroup="head"><md-table-cell head scope="col">Client</md-table-cell><md-table-cell head scope="col" numeric>Amount</md-table-cell></md-table-row></md-table-head><md-table-body><md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row><md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row></md-table-body></md-table></md-table-container>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-4</span>
<md-table-container variant="outlined"><md-table column-template="2fr 1fr" density="-4" label="Rung -4"><md-table-head><md-table-row rowgroup="head"><md-table-cell head scope="col">Client</md-table-cell><md-table-cell head scope="col" numeric>Amount</md-table-cell></md-table-row></md-table-head><md-table-body><md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row><md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row></md-table-body></md-table></md-table-container>
</div>import { MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead, MdTableRow } from '@awc-ui/react';
export function Demo() {
return (
<>
<div style={{ display: 'grid', gridTemplateColumns: 'auto 1fr', gap: '14px 16px', alignItems: 'center' }}>
<span style={{ inlineSize: '3.5rem', opacity: '.65', fontSize: '.75rem', fontFamily: 'ui-monospace,monospace' }}>0</span>
<MdTableContainer variant="outlined"><MdTable columnTemplate="2fr 1fr" density="0" label="Rung 0"><MdTableHead><MdTableRow rowgroup="head"><MdTableCell head scope="col">Client</MdTableCell><MdTableCell head scope="col" numeric>Amount</MdTableCell></MdTableRow></MdTableHead><MdTableBody><MdTableRow><MdTableCell head scope="row">Acme Corp</MdTableCell><MdTableCell numeric>$1,200.00</MdTableCell></MdTableRow><MdTableRow><MdTableCell head scope="row">Globex</MdTableCell><MdTableCell numeric>$3,120.00</MdTableCell></MdTableRow></MdTableBody></MdTable></MdTableContainer>
<span style={{ inlineSize: '3.5rem', opacity: '.65', fontSize: '.75rem', fontFamily: 'ui-monospace,monospace' }}>-1</span>
<MdTableContainer variant="outlined"><MdTable columnTemplate="2fr 1fr" density="-1" label="Rung -1"><MdTableHead><MdTableRow rowgroup="head"><MdTableCell head scope="col">Client</MdTableCell><MdTableCell head scope="col" numeric>Amount</MdTableCell></MdTableRow></MdTableHead><MdTableBody><MdTableRow><MdTableCell head scope="row">Acme Corp</MdTableCell><MdTableCell numeric>$1,200.00</MdTableCell></MdTableRow><MdTableRow><MdTableCell head scope="row">Globex</MdTableCell><MdTableCell numeric>$3,120.00</MdTableCell></MdTableRow></MdTableBody></MdTable></MdTableContainer>
<span style={{ inlineSize: '3.5rem', opacity: '.65', fontSize: '.75rem', fontFamily: 'ui-monospace,monospace' }}>-2</span>
<MdTableContainer variant="outlined"><MdTable columnTemplate="2fr 1fr" density="-2" label="Rung -2"><MdTableHead><MdTableRow rowgroup="head"><MdTableCell head scope="col">Client</MdTableCell><MdTableCell head scope="col" numeric>Amount</MdTableCell></MdTableRow></MdTableHead><MdTableBody><MdTableRow><MdTableCell head scope="row">Acme Corp</MdTableCell><MdTableCell numeric>$1,200.00</MdTableCell></MdTableRow><MdTableRow><MdTableCell head scope="row">Globex</MdTableCell><MdTableCell numeric>$3,120.00</MdTableCell></MdTableRow></MdTableBody></MdTable></MdTableContainer>
<span style={{ inlineSize: '3.5rem', opacity: '.65', fontSize: '.75rem', fontFamily: 'ui-monospace,monospace' }}>-3</span>
<MdTableContainer variant="outlined"><MdTable columnTemplate="2fr 1fr" density="-3" label="Rung -3"><MdTableHead><MdTableRow rowgroup="head"><MdTableCell head scope="col">Client</MdTableCell><MdTableCell head scope="col" numeric>Amount</MdTableCell></MdTableRow></MdTableHead><MdTableBody><MdTableRow><MdTableCell head scope="row">Acme Corp</MdTableCell><MdTableCell numeric>$1,200.00</MdTableCell></MdTableRow><MdTableRow><MdTableCell head scope="row">Globex</MdTableCell><MdTableCell numeric>$3,120.00</MdTableCell></MdTableRow></MdTableBody></MdTable></MdTableContainer>
<span style={{ inlineSize: '3.5rem', opacity: '.65', fontSize: '.75rem', fontFamily: 'ui-monospace,monospace' }}>-4</span>
<MdTableContainer variant="outlined"><MdTable columnTemplate="2fr 1fr" density="-4" label="Rung -4"><MdTableHead><MdTableRow rowgroup="head"><MdTableCell head scope="col">Client</MdTableCell><MdTableCell head scope="col" numeric>Amount</MdTableCell></MdTableRow></MdTableHead><MdTableBody><MdTableRow><MdTableCell head scope="row">Acme Corp</MdTableCell><MdTableCell numeric>$1,200.00</MdTableCell></MdTableRow><MdTableRow><MdTableCell head scope="row">Globex</MdTableCell><MdTableCell numeric>$3,120.00</MdTableCell></MdTableRow></MdTableBody></MdTable></MdTableContainer>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div style="display:grid;grid-template-columns:auto 1fr;gap:14px 16px;align-items:center;">
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">0</span>
<md-table-container variant="outlined"><md-table column-template="2fr 1fr" density="0" label="Rung 0"><md-table-head><md-table-row rowgroup="head"><md-table-cell head scope="col">Client</md-table-cell><md-table-cell head scope="col" numeric>Amount</md-table-cell></md-table-row></md-table-head><md-table-body><md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row><md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row></md-table-body></md-table></md-table-container>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-1</span>
<md-table-container variant="outlined"><md-table column-template="2fr 1fr" density="-1" label="Rung -1"><md-table-head><md-table-row rowgroup="head"><md-table-cell head scope="col">Client</md-table-cell><md-table-cell head scope="col" numeric>Amount</md-table-cell></md-table-row></md-table-head><md-table-body><md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row><md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row></md-table-body></md-table></md-table-container>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-2</span>
<md-table-container variant="outlined"><md-table column-template="2fr 1fr" density="-2" label="Rung -2"><md-table-head><md-table-row rowgroup="head"><md-table-cell head scope="col">Client</md-table-cell><md-table-cell head scope="col" numeric>Amount</md-table-cell></md-table-row></md-table-head><md-table-body><md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row><md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row></md-table-body></md-table></md-table-container>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-3</span>
<md-table-container variant="outlined"><md-table column-template="2fr 1fr" density="-3" label="Rung -3"><md-table-head><md-table-row rowgroup="head"><md-table-cell head scope="col">Client</md-table-cell><md-table-cell head scope="col" numeric>Amount</md-table-cell></md-table-row></md-table-head><md-table-body><md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row><md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row></md-table-body></md-table></md-table-container>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-4</span>
<md-table-container variant="outlined"><md-table column-template="2fr 1fr" density="-4" label="Rung -4"><md-table-head><md-table-row rowgroup="head"><md-table-cell head scope="col">Client</md-table-cell><md-table-cell head scope="col" numeric>Amount</md-table-cell></md-table-row></md-table-head><md-table-body><md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row><md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row></md-table-body></md-table></md-table-container>
</div><script setup>
import '@awc-ui/core/define';
</script>
<template>
<div style="display:grid;grid-template-columns:auto 1fr;gap:14px 16px;align-items:center;">
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">0</span>
<md-table-container variant="outlined"><md-table column-template="2fr 1fr" density="0" label="Rung 0"><md-table-head><md-table-row rowgroup="head"><md-table-cell head scope="col">Client</md-table-cell><md-table-cell head scope="col" numeric>Amount</md-table-cell></md-table-row></md-table-head><md-table-body><md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row><md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row></md-table-body></md-table></md-table-container>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-1</span>
<md-table-container variant="outlined"><md-table column-template="2fr 1fr" density="-1" label="Rung -1"><md-table-head><md-table-row rowgroup="head"><md-table-cell head scope="col">Client</md-table-cell><md-table-cell head scope="col" numeric>Amount</md-table-cell></md-table-row></md-table-head><md-table-body><md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row><md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row></md-table-body></md-table></md-table-container>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-2</span>
<md-table-container variant="outlined"><md-table column-template="2fr 1fr" density="-2" label="Rung -2"><md-table-head><md-table-row rowgroup="head"><md-table-cell head scope="col">Client</md-table-cell><md-table-cell head scope="col" numeric>Amount</md-table-cell></md-table-row></md-table-head><md-table-body><md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row><md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row></md-table-body></md-table></md-table-container>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-3</span>
<md-table-container variant="outlined"><md-table column-template="2fr 1fr" density="-3" label="Rung -3"><md-table-head><md-table-row rowgroup="head"><md-table-cell head scope="col">Client</md-table-cell><md-table-cell head scope="col" numeric>Amount</md-table-cell></md-table-row></md-table-head><md-table-body><md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row><md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row></md-table-body></md-table></md-table-container>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-4</span>
<md-table-container variant="outlined"><md-table column-template="2fr 1fr" density="-4" label="Rung -4"><md-table-head><md-table-row rowgroup="head"><md-table-cell head scope="col">Client</md-table-cell><md-table-cell head scope="col" numeric>Amount</md-table-cell></md-table-row></md-table-head><md-table-body><md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row><md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row></md-table-body></md-table></md-table-container>
</div>
</template><script>
import '@awc-ui/core/define';
</script>
<div style="display:grid;grid-template-columns:auto 1fr;gap:14px 16px;align-items:center;">
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">0</span>
<md-table-container variant="outlined"><md-table column-template="2fr 1fr" density="0" label="Rung 0"><md-table-head><md-table-row rowgroup="head"><md-table-cell head scope="col">Client</md-table-cell><md-table-cell head scope="col" numeric>Amount</md-table-cell></md-table-row></md-table-head><md-table-body><md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row><md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row></md-table-body></md-table></md-table-container>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-1</span>
<md-table-container variant="outlined"><md-table column-template="2fr 1fr" density="-1" label="Rung -1"><md-table-head><md-table-row rowgroup="head"><md-table-cell head scope="col">Client</md-table-cell><md-table-cell head scope="col" numeric>Amount</md-table-cell></md-table-row></md-table-head><md-table-body><md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row><md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row></md-table-body></md-table></md-table-container>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-2</span>
<md-table-container variant="outlined"><md-table column-template="2fr 1fr" density="-2" label="Rung -2"><md-table-head><md-table-row rowgroup="head"><md-table-cell head scope="col">Client</md-table-cell><md-table-cell head scope="col" numeric>Amount</md-table-cell></md-table-row></md-table-head><md-table-body><md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row><md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row></md-table-body></md-table></md-table-container>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-3</span>
<md-table-container variant="outlined"><md-table column-template="2fr 1fr" density="-3" label="Rung -3"><md-table-head><md-table-row rowgroup="head"><md-table-cell head scope="col">Client</md-table-cell><md-table-cell head scope="col" numeric>Amount</md-table-cell></md-table-row></md-table-head><md-table-body><md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row><md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row></md-table-body></md-table></md-table-container>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-4</span>
<md-table-container variant="outlined"><md-table column-template="2fr 1fr" density="-4" label="Rung -4"><md-table-head><md-table-row rowgroup="head"><md-table-cell head scope="col">Client</md-table-cell><md-table-cell head scope="col" numeric>Amount</md-table-cell></md-table-row></md-table-head><md-table-body><md-table-row><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row><md-table-row><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row></md-table-body></md-table></md-table-container>
</div>The two are independent signals, so they compose without any extra wiring:
dir="rtl" and data-density="-2" on the same ancestor. The second table asks
for comfortable rows inside that region — and because the semantic sizes are
themselves scale-aware, it relaxes relative to the rung rather than escaping
it:
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div dir="rtl" data-density="-2" style="display:grid;gap:16px;">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" striped label="الفواتير">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">العميل</md-table-cell>
<md-table-cell head scope="col" numeric>المبلغ</md-table-cell>
<md-table-cell head scope="col">الحالة</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="d1"><md-table-cell head scope="row">أكمي</md-table-cell><md-table-cell numeric>١٢٠٠٫٠٠</md-table-cell><md-table-cell>مدفوع</md-table-cell></md-table-row>
<md-table-row value="d2"><md-table-cell head scope="row">غلوبكس</md-table-cell><md-table-cell numeric>٣١٢٠٫٠٠</md-table-cell><md-table-cell>معلق</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" striped density="comfortable" label="الفواتير — مريح">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">العميل</md-table-cell>
<md-table-cell head scope="col" numeric>المبلغ</md-table-cell>
<md-table-cell head scope="col">الحالة</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="d3"><md-table-cell head scope="row">أكمي</md-table-cell><md-table-cell numeric>١٢٠٠٫٠٠</md-table-cell><md-table-cell>مدفوع</md-table-cell></md-table-row>
<md-table-row value="d4"><md-table-cell head scope="row">غلوبكس</md-table-cell><md-table-cell numeric>٣١٢٠٫٠٠</md-table-cell><md-table-cell>معلق</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>import { MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead, MdTableRow } from '@awc-ui/react';
export function Demo() {
return (
<>
<div dir="rtl" data-density="-2" style={{ display: 'grid', gap: '16px' }}>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="2fr 1fr 1fr" striped label="الفواتير">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">العميل</MdTableCell>
<MdTableCell head scope="col" numeric>المبلغ</MdTableCell>
<MdTableCell head scope="col">الحالة</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="d1"><MdTableCell head scope="row">أكمي</MdTableCell><MdTableCell numeric>١٢٠٠٫٠٠</MdTableCell><MdTableCell>مدفوع</MdTableCell></MdTableRow>
<MdTableRow value="d2"><MdTableCell head scope="row">غلوبكس</MdTableCell><MdTableCell numeric>٣١٢٠٫٠٠</MdTableCell><MdTableCell>معلق</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="2fr 1fr 1fr" striped density="comfortable" label="الفواتير — مريح">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">العميل</MdTableCell>
<MdTableCell head scope="col" numeric>المبلغ</MdTableCell>
<MdTableCell head scope="col">الحالة</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="d3"><MdTableCell head scope="row">أكمي</MdTableCell><MdTableCell numeric>١٢٠٠٫٠٠</MdTableCell><MdTableCell>مدفوع</MdTableCell></MdTableRow>
<MdTableRow value="d4"><MdTableCell head scope="row">غلوبكس</MdTableCell><MdTableCell numeric>٣١٢٠٫٠٠</MdTableCell><MdTableCell>معلق</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div dir="rtl" data-density="-2" style="display:grid;gap:16px;">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" striped label="الفواتير">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">العميل</md-table-cell>
<md-table-cell head scope="col" numeric>المبلغ</md-table-cell>
<md-table-cell head scope="col">الحالة</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="d1"><md-table-cell head scope="row">أكمي</md-table-cell><md-table-cell numeric>١٢٠٠٫٠٠</md-table-cell><md-table-cell>مدفوع</md-table-cell></md-table-row>
<md-table-row value="d2"><md-table-cell head scope="row">غلوبكس</md-table-cell><md-table-cell numeric>٣١٢٠٫٠٠</md-table-cell><md-table-cell>معلق</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" striped density="comfortable" label="الفواتير — مريح">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">العميل</md-table-cell>
<md-table-cell head scope="col" numeric>المبلغ</md-table-cell>
<md-table-cell head scope="col">الحالة</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="d3"><md-table-cell head scope="row">أكمي</md-table-cell><md-table-cell numeric>١٢٠٠٫٠٠</md-table-cell><md-table-cell>مدفوع</md-table-cell></md-table-row>
<md-table-row value="d4"><md-table-cell head scope="row">غلوبكس</md-table-cell><md-table-cell numeric>٣١٢٠٫٠٠</md-table-cell><md-table-cell>معلق</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div><script setup>
import '@awc-ui/core/define';
</script>
<template>
<div dir="rtl" data-density="-2" style="display:grid;gap:16px;">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" striped label="الفواتير">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">العميل</md-table-cell>
<md-table-cell head scope="col" numeric>المبلغ</md-table-cell>
<md-table-cell head scope="col">الحالة</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="d1"><md-table-cell head scope="row">أكمي</md-table-cell><md-table-cell numeric>١٢٠٠٫٠٠</md-table-cell><md-table-cell>مدفوع</md-table-cell></md-table-row>
<md-table-row value="d2"><md-table-cell head scope="row">غلوبكس</md-table-cell><md-table-cell numeric>٣١٢٠٫٠٠</md-table-cell><md-table-cell>معلق</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" striped density="comfortable" label="الفواتير — مريح">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">العميل</md-table-cell>
<md-table-cell head scope="col" numeric>المبلغ</md-table-cell>
<md-table-cell head scope="col">الحالة</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="d3"><md-table-cell head scope="row">أكمي</md-table-cell><md-table-cell numeric>١٢٠٠٫٠٠</md-table-cell><md-table-cell>مدفوع</md-table-cell></md-table-row>
<md-table-row value="d4"><md-table-cell head scope="row">غلوبكس</md-table-cell><md-table-cell numeric>٣١٢٠٫٠٠</md-table-cell><md-table-cell>معلق</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
</template><script>
import '@awc-ui/core/define';
</script>
<div dir="rtl" data-density="-2" style="display:grid;gap:16px;">
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" striped label="الفواتير">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">العميل</md-table-cell>
<md-table-cell head scope="col" numeric>المبلغ</md-table-cell>
<md-table-cell head scope="col">الحالة</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="d1"><md-table-cell head scope="row">أكمي</md-table-cell><md-table-cell numeric>١٢٠٠٫٠٠</md-table-cell><md-table-cell>مدفوع</md-table-cell></md-table-row>
<md-table-row value="d2"><md-table-cell head scope="row">غلوبكس</md-table-cell><md-table-cell numeric>٣١٢٠٫٠٠</md-table-cell><md-table-cell>معلق</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" striped density="comfortable" label="الفواتير — مريح">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">العميل</md-table-cell>
<md-table-cell head scope="col" numeric>المبلغ</md-table-cell>
<md-table-cell head scope="col">الحالة</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="d3"><md-table-cell head scope="row">أكمي</md-table-cell><md-table-cell numeric>١٢٠٠٫٠٠</md-table-cell><md-table-cell>مدفوع</md-table-cell></md-table-row>
<md-table-row value="d4"><md-table-cell head scope="row">غلوبكس</md-table-cell><md-table-cell numeric>٣١٢٠٫٠٠</md-table-cell><md-table-cell>معلق</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>Headers, cells and the empty state are all your content, so they come straight
from your i18n layer. Only the pagination and expand-toggle chrome has label
props of its own — and md-table-pagination’s use percent tokens
(%from%, %to%, %count%), not the braces used elsewhere in the library.
The localization story switches the lot.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label="Rechnungen">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Kunde</md-table-cell>
<md-table-cell head scope="col" numeric>Betrag</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body id="loc-body">
<md-table-row value="l1"><md-table-cell head scope="row">Acme GmbH</md-table-cell><md-table-cell numeric>1.200,00 €</md-table-cell><md-table-cell>Bezahlt</md-table-cell></md-table-row>
<md-table-row value="l2"><md-table-cell head scope="row">Globex AG</md-table-cell><md-table-cell numeric>3.120,00 €</md-table-cell><md-table-cell>Offen</md-table-cell></md-table-row>
<md-table-row value="l3"><md-table-cell head scope="row">Initech KG</md-table-cell><md-table-cell numeric>860,00 €</md-table-cell><md-table-cell>Bezahlt</md-table-cell></md-table-row>
<md-table-row value="l4"><md-table-cell head scope="row">Umbrella SE</md-table-cell><md-table-cell numeric>2.450,00 €</md-table-cell><md-table-cell>Entwurf</md-table-cell></md-table-row>
<md-table-row value="l5"><md-table-cell head scope="row">Soylent GmbH</md-table-cell><md-table-cell numeric>1.975,00 €</md-table-cell><md-table-cell>Bezahlt</md-table-cell></md-table-row>
<md-table-row value="l6"><md-table-cell head scope="row">Hooli AG</md-table-cell><md-table-cell numeric>4.300,00 €</md-table-cell><md-table-cell>Offen</md-table-cell></md-table-row>
<md-table-row value="l7"><md-table-cell head scope="row">Stark Industries</md-table-cell><md-table-cell numeric>8.120,00 €</md-table-cell><md-table-cell>Bezahlt</md-table-cell></md-table-row>
<md-table-row value="l8"><md-table-cell head scope="row">Wayne Enterprises</md-table-cell><md-table-cell numeric>6.640,00 €</md-table-cell><md-table-cell>Entwurf</md-table-cell></md-table-row>
<md-table-row value="l9"><md-table-cell head scope="row">Cyberdyne GmbH</md-table-cell><md-table-cell numeric>2.010,00 €</md-table-cell><md-table-cell>Bezahlt</md-table-cell></md-table-row>
<md-table-row value="l10"><md-table-cell head scope="row">Tyrell Corp</md-table-cell><md-table-cell numeric>3.780,00 €</md-table-cell><md-table-cell>Offen</md-table-cell></md-table-row>
</md-table-body>
</md-table>
<md-table-pagination id="loc-pag" slot="bottom" count="10" page="0" rows-per-page="5" label-rows-per-page="Zeilen pro Seite:" label-displayed-rows="%from%–%to% von %count%"></md-table-pagination>
</md-table-container>
<script type="module">
var pag = document.getElementById('loc-pag');
var body = document.getElementById('loc-body');
var page = 0, perPage = 5;
var renderPage = function () {
var rows = Array.prototype.slice.call(body.querySelectorAll('md-table-row'));
rows.forEach(function (r, i) { r.style.display = i >= page * perPage && i < (page + 1) * perPage ? '' : 'none'; });
};
pag.addEventListener('mdPageChange', function (e) { page = e.detail.page; renderPage(); });
pag.addEventListener('mdRowsPerPageChange', function (e) { perPage = e.detail.rowsPerPage; page = 0; renderPage(); });
renderPage();
</script>import { useState } from 'react';
import {
MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead, MdTablePagination, MdTableRow,
} from '@awc-ui/react';
const LOCALE = 'de-DE';
const money = (n) => n.toLocaleString(LOCALE, { style: 'currency', currency: 'EUR' });
// Content is yours to translate; the pagination chrome takes label props.
const T = {
headline: 'Rechnungen',
client: 'Kunde', amount: 'Betrag', status: 'Status',
rowsPerPage: 'Zeilen pro Seite:',
displayedRows: '%from%–%to% von %count%',
};
const INVOICES = [
{ id: 'l1', client: 'Acme GmbH', amount: 1200, status: 'Bezahlt' },
{ id: 'l2', client: 'Globex AG', amount: 3120, status: 'Offen' },
{ id: 'l3', client: 'Initech KG', amount: 860, status: 'Bezahlt' },
// … seven more …
];
export function LocalizedInvoices() {
const [page, setPage] = useState(0);
const [perPage, setPerPage] = useState(5);
const pageRows = INVOICES.slice(page * perPage, (page + 1) * perPage);
return (
<MdTableContainer variant="outlined">
<MdTable columnTemplate="2fr 1fr 1fr" label={T.headline} rowCount={INVOICES.length} rowOffset={page * perPage}>
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">{T.client}</MdTableCell>
<MdTableCell head scope="col" numeric>{T.amount}</MdTableCell>
<MdTableCell head scope="col">{T.status}</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
{pageRows.map((row) => (
<MdTableRow key={row.id} value={row.id}>
<MdTableCell head scope="row">{row.client}</MdTableCell>
{/* Intl formats the number; the component never assumes a locale. */}
<MdTableCell numeric>{money(row.amount)}</MdTableCell>
<MdTableCell>{row.status}</MdTableCell>
</MdTableRow>
))}
</MdTableBody>
</MdTable>
<MdTablePagination
slot="bottom"
count={INVOICES.length}
page={page}
rowsPerPage={perPage}
labelRowsPerPage={T.rowsPerPage}
labelDisplayedRows={T.displayedRows}
onMdPageChange={(e) => setPage(e.detail.page)}
onMdRowsPerPageChange={(e) => { setPerPage(e.detail.rowsPerPage); setPage(0); }}
/>
</MdTableContainer>
);
}// localized-invoices.component.ts
import { Component, CUSTOM_ELEMENTS_SCHEMA, LOCALE_ID } from '@angular/core';
import { CommonModule, registerLocaleData } from '@angular/common';
import localeDe from '@angular/common/locales/de';
import { defineCustomElements } from '@awc-ui/core/loader';
registerLocaleData(localeDe);
defineCustomElements(window);
interface Invoice { id: string; client: string; amount: number; status: string; }
@Component({
selector: 'app-localized-invoices',
standalone: true,
imports: [CommonModule],
providers: [{ provide: LOCALE_ID, useValue: 'de-DE' }],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" [attr.label]="t.headline"
[attr.row-count]="invoices.length" [attr.row-offset]="page * perPage">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">{{ t.client }}</md-table-cell>
<md-table-cell head scope="col" numeric>{{ t.amount }}</md-table-cell>
<md-table-cell head scope="col">{{ t.status }}</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row *ngFor="let row of pageRows; trackBy: trackById" [attr.value]="row.id">
<md-table-cell head scope="row">{{ row.client }}</md-table-cell>
<md-table-cell numeric>{{ row.amount | currency:'EUR' }}</md-table-cell>
<md-table-cell>{{ row.status }}</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
<!-- %from%/%to%/%count% are substituted, so number order can differ per language. -->
<md-table-pagination
slot="bottom"
[attr.count]="invoices.length"
[attr.page]="page"
[attr.rows-per-page]="perPage"
[attr.label-rows-per-page]="t.rowsPerPage"
[attr.label-displayed-rows]="t.displayedRows"
(mdPageChange)="page = $any($event).detail.page"
(mdRowsPerPageChange)="perPage = $any($event).detail.rowsPerPage; page = 0"
></md-table-pagination>
</md-table-container>
`,
})
export class LocalizedInvoicesComponent {
t = {
headline: 'Rechnungen', client: 'Kunde', amount: 'Betrag', status: 'Status',
rowsPerPage: 'Zeilen pro Seite:', displayedRows: '%from%–%to% von %count%',
};
invoices: Invoice[] = [
{ id: 'l1', client: 'Acme GmbH', amount: 1200, status: 'Bezahlt' },
{ id: 'l2', client: 'Globex AG', amount: 3120, status: 'Offen' },
{ id: 'l3', client: 'Initech KG', amount: 860, status: 'Bezahlt' },
];
page = 0;
perPage = 5;
get pageRows(): Invoice[] {
return this.invoices.slice(this.page * this.perPage, (this.page + 1) * this.perPage);
}
trackById = (_: number, row: Invoice) => row.id;
}<script setup>
import { computed, ref } from 'vue';
import '@awc-ui/core/define';
const LOCALE = 'de-DE';
const money = (n) => n.toLocaleString(LOCALE, { style: 'currency', currency: 'EUR' });
// Content is yours to translate; the pagination chrome takes label props.
const T = {
headline: 'Rechnungen',
client: 'Kunde', amount: 'Betrag', status: 'Status',
rowsPerPage: 'Zeilen pro Seite:',
displayedRows: '%from%–%to% von %count%',
};
const INVOICES = [
{ id: 'l1', client: 'Acme GmbH', amount: 1200, status: 'Bezahlt' },
{ id: 'l2', client: 'Globex AG', amount: 3120, status: 'Offen' },
{ id: 'l3', client: 'Initech KG', amount: 860, status: 'Bezahlt' },
// … seven more …
];
const page = ref(0);
const perPage = ref(5);
const pageRows = computed(() =>
INVOICES.slice(page.value * perPage.value, (page.value + 1) * perPage.value));
</script>
<template>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" :label="T.headline"
:row-count="INVOICES.length" :row-offset="page * perPage">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">{{ T.client }}</md-table-cell>
<md-table-cell head scope="col" numeric>{{ T.amount }}</md-table-cell>
<md-table-cell head scope="col">{{ T.status }}</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row v-for="row in pageRows" :key="row.id" :value="row.id">
<md-table-cell head scope="row">{{ row.client }}</md-table-cell>
<md-table-cell numeric>{{ money(row.amount) }}</md-table-cell>
<md-table-cell>{{ row.status }}</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
<md-table-pagination
slot="bottom"
:count="INVOICES.length"
:page="page"
:rows-per-page="perPage"
:label-rows-per-page="T.rowsPerPage"
:label-displayed-rows="T.displayedRows"
@mdPageChange="page = $event.detail.page"
@mdRowsPerPageChange="perPage = $event.detail.rowsPerPage; page = 0"
></md-table-pagination>
</md-table-container>
</template><script>
import '@awc-ui/core/define';
const LOCALE = 'de-DE';
const money = (n) => n.toLocaleString(LOCALE, { style: 'currency', currency: 'EUR' });
// Content is yours to translate; the pagination chrome takes label props.
const T = {
headline: 'Rechnungen',
client: 'Kunde', amount: 'Betrag', status: 'Status',
rowsPerPage: 'Zeilen pro Seite:',
displayedRows: '%from%–%to% von %count%',
};
const INVOICES = [
{ id: 'l1', client: 'Acme GmbH', amount: 1200, status: 'Bezahlt' },
{ id: 'l2', client: 'Globex AG', amount: 3120, status: 'Offen' },
{ id: 'l3', client: 'Initech KG', amount: 860, status: 'Bezahlt' },
// … seven more …
];
let page = 0;
let perPage = 5;
$: pageRows = INVOICES.slice(page * perPage, (page + 1) * perPage);
</script>
<md-table-container variant="outlined">
<md-table column-template="2fr 1fr 1fr" label={T.headline}
row-count={INVOICES.length} row-offset={page * perPage}>
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">{T.client}</md-table-cell>
<md-table-cell head scope="col" numeric>{T.amount}</md-table-cell>
<md-table-cell head scope="col">{T.status}</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
{#each pageRows as row (row.id)}
<md-table-row value={row.id}>
<md-table-cell head scope="row">{row.client}</md-table-cell>
<md-table-cell numeric>{money(row.amount)}</md-table-cell>
<md-table-cell>{row.status}</md-table-cell>
</md-table-row>
{/each}
</md-table-body>
</md-table>
<md-table-pagination
slot="bottom"
count={INVOICES.length}
{page}
rows-per-page={perPage}
label-rows-per-page={T.rowsPerPage}
label-displayed-rows={T.displayedRows}
on:mdPageChange={(e) => (page = e.detail.page)}
on:mdRowsPerPageChange={(e) => { perPage = e.detail.rowsPerPage; page = 0; }}
></md-table-pagination>
</md-table-container>Density — dual-typed here: compact / standard / comfortable or the
numeric rungs -1…-4, which locally override the inherited data-density
(0 is the uncompacted default, not a rung — it inherits). Row
heights are also settable directly via --md-table-row-height-*. See
Density and the Density section above.
i18n — headers, cells and the empty state are your slotted content, so they
come from your i18n layer; lang and dir are inherited from any ancestor.
Localize every aria-label on the row checkboxes and expand toggles too, and
prefer fr units over fixed px in column-template so longer translations
still fit.
| Custom property | Purpose | Default |
|---|---|---|
--md-table-row-height-compact / -standard / -comfortable | Row heights per semantic density | 36px / 52px / 60px, each shifted by the density rung (compact floors at 28px, comfortable at 40px) |
--md-table-header-height | Header row height | 56px, density-shifted, floored at 40px |
--md-table-cell-padding-inline / -padding-block | Cell padding | 16px / 8px at standard (12px / 4px compact, 20px / 8px comfortable) |
--md-table-divider-color | Row rules (also toggled by no-dividers) | outline-variant |
--md-table-stripe-color | Alternate rows when striped | on-surface at half the hover state-layer opacity |
--md-table-head-bg / --md-table-head-color | Header band | surface-container / on-surface-variant |
--md-table-foot-bg | Footer band | surface-container-low |
--md-table-row-hover-color | Row hover state-layer colour, mixed at the M3 hover opacity (hoverable, on by default) | on-surface |
--md-table-row-selected-bg / -selected-color | Selected rows | secondary-container / on-secondary-container |
--md-table-min-width | Same floor as the min-width prop | max-content |
--md-table-columns-template | Track template, as a custom property | derived from the column-template prop |
--md-table-body-max-h | Body cap in frozen mode | none |
--md-table-empty-min-height | Height reserved for the empty state | 160px |
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined" style="--md-table-head-bg: var(--md-sys-color-primary-container); --md-table-head-color: var(--md-sys-color-on-primary-container); --md-table-row-hover-color: var(--md-sys-color-primary); --md-table-stripe-color: var(--md-sys-color-surface-container-high); --md-table-divider-color: var(--md-sys-color-primary);">
<md-table column-template="2fr 1fr 1fr" label="Custom properties">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>import { MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead, MdTableRow } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdTableContainer variant="outlined" style={{ '--md-table-head-bg': 'var(--md-sys-color-primary-container)', '--md-table-head-color': 'var(--md-sys-color-on-primary-container)', '--md-table-row-hover-color': 'var(--md-sys-color-primary)', '--md-table-stripe-color': 'var(--md-sys-color-surface-container-high)', '--md-table-divider-color': 'var(--md-sys-color-primary)' }}>
<MdTable columnTemplate="2fr 1fr 1fr" label="Custom properties">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="r1"><MdTableCell head scope="row">Acme Corp</MdTableCell><MdTableCell numeric>$1,200.00</MdTableCell><MdTableCell>Paid</MdTableCell></MdTableRow>
<MdTableRow value="r2"><MdTableCell head scope="row">Globex</MdTableCell><MdTableCell numeric>$3,120.00</MdTableCell><MdTableCell>Pending</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-table-container variant="outlined" style="--md-table-head-bg: var(--md-sys-color-primary-container); --md-table-head-color: var(--md-sys-color-on-primary-container); --md-table-row-hover-color: var(--md-sys-color-primary); --md-table-stripe-color: var(--md-sys-color-surface-container-high); --md-table-divider-color: var(--md-sys-color-primary);">
<md-table column-template="2fr 1fr 1fr" label="Custom properties">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-table-container variant="outlined" style="--md-table-head-bg: var(--md-sys-color-primary-container); --md-table-head-color: var(--md-sys-color-on-primary-container); --md-table-row-hover-color: var(--md-sys-color-primary); --md-table-stripe-color: var(--md-sys-color-surface-container-high); --md-table-divider-color: var(--md-sys-color-primary);">
<md-table column-template="2fr 1fr 1fr" label="Custom properties">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</template><script>
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined" style="--md-table-head-bg: var(--md-sys-color-primary-container); --md-table-head-color: var(--md-sys-color-on-primary-container); --md-table-row-hover-color: var(--md-sys-color-primary); --md-table-stripe-color: var(--md-sys-color-surface-container-high); --md-table-divider-color: var(--md-sys-color-primary);">
<md-table column-template="2fr 1fr 1fr" label="Custom properties">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="r1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="r2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container><!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div style="inline-size: 100%; max-inline-size: 320px;">
<md-table-container variant="outlined">
<md-table column-template="1fr auto" label="In a narrow column">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="n1"><md-table-cell head scope="row" ellipsis>A client with a very long name</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row>
<md-table-row value="n2"><md-table-cell head scope="row" ellipsis>Globex International Holdings</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>import { MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead, MdTableRow } from '@awc-ui/react';
export function Demo() {
return (
<>
<div style={{ inlineSize: '100%', maxInlineSize: '320px' }}>
<MdTableContainer variant="outlined">
<MdTable columnTemplate="1fr auto" label="In a narrow column">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="n1"><MdTableCell head scope="row" ellipsis>A client with a very long name</MdTableCell><MdTableCell numeric>$1,200.00</MdTableCell></MdTableRow>
<MdTableRow value="n2"><MdTableCell head scope="row" ellipsis>Globex International Holdings</MdTableCell><MdTableCell numeric>$3,120.00</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div style="inline-size: 100%; max-inline-size: 320px;">
<md-table-container variant="outlined">
<md-table column-template="1fr auto" label="In a narrow column">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="n1"><md-table-cell head scope="row" ellipsis>A client with a very long name</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row>
<md-table-row value="n2"><md-table-cell head scope="row" ellipsis>Globex International Holdings</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div><script setup>
import '@awc-ui/core/define';
</script>
<template>
<div style="inline-size: 100%; max-inline-size: 320px;">
<md-table-container variant="outlined">
<md-table column-template="1fr auto" label="In a narrow column">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="n1"><md-table-cell head scope="row" ellipsis>A client with a very long name</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row>
<md-table-row value="n2"><md-table-cell head scope="row" ellipsis>Globex International Holdings</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>
</template><script>
import '@awc-ui/core/define';
</script>
<div style="inline-size: 100%; max-inline-size: 320px;">
<md-table-container variant="outlined">
<md-table column-template="1fr auto" label="In a narrow column">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="n1"><md-table-cell head scope="row" ellipsis>A client with a very long name</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell></md-table-row>
<md-table-row value="n2"><md-table-cell head scope="row" ellipsis>Globex International Holdings</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</div>Two more theme treatments in Storybook: vibrant and dark theme. The responsive story steps a table through the breakpoints, and custom CSS collects the property overrides in one place.
CSS parts reach inside the shadow root for what custom properties don’t
cover — grid, header-grid, caption, empty, skeleton-row, loading,
progress, hscrollbar, hscrollbar-thumb, vscrollbar, vscrollbar-thumb.
header-grid and the two scrollbar pairs exist only in frozen-header mode
(that’s the layout that splits the header off and draws its own scrollbars), and
caption only renders when caption is set:
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<style>
.parted::part(caption) {
color: var(--md-sys-color-primary);
font-weight: 600;
letter-spacing: .06em;
text-transform: uppercase;
}
.parted::part(header-grid) { box-shadow: 0 2px 0 var(--md-sys-color-primary); }
.parted::part(grid) { background: var(--md-sys-color-surface-container-lowest); }
.parted::part(vscrollbar-thumb) { background: var(--md-sys-color-primary); }
</style>
<md-table-container variant="outlined" max-height="240px">
<md-table class="parted" frozen-header caption="Q3 invoices" column-template="2fr 1fr 1fr" label="Themed with CSS parts">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="c1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="c2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
<md-table-row value="c3"><md-table-cell head scope="row">Initech</md-table-cell><md-table-cell numeric>$860.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="c4"><md-table-cell head scope="row">Umbrella</md-table-cell><md-table-cell numeric>$2,450.00</md-table-cell><md-table-cell>Draft</md-table-cell></md-table-row>
<md-table-row value="c5"><md-table-cell head scope="row">Soylent</md-table-cell><md-table-cell numeric>$1,975.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="c6"><md-table-cell head scope="row">Hooli</md-table-cell><md-table-cell numeric>$4,300.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>import { MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableHead, MdTableRow } from '@awc-ui/react';
export function Demo() {
return (
<>
<style>
.parted::part(caption) {
color: var(--md-sys-color-primary);
font-weight: 600;
letter-spacing: .06em;
text-transform: uppercase;
}
.parted::part(header-grid) { box-shadow: 0 2px 0 var(--md-sys-color-primary); }
.parted::part(grid) { background: var(--md-sys-color-surface-container-lowest); }
.parted::part(vscrollbar-thumb) { background: var(--md-sys-color-primary); }
</style>
<MdTableContainer variant="outlined" maxHeight="240px">
<MdTable className="parted" frozenHeader caption="Q3 invoices" columnTemplate="2fr 1fr 1fr" label="Themed with CSS parts">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="c1"><MdTableCell head scope="row">Acme Corp</MdTableCell><MdTableCell numeric>$1,200.00</MdTableCell><MdTableCell>Paid</MdTableCell></MdTableRow>
<MdTableRow value="c2"><MdTableCell head scope="row">Globex</MdTableCell><MdTableCell numeric>$3,120.00</MdTableCell><MdTableCell>Pending</MdTableCell></MdTableRow>
<MdTableRow value="c3"><MdTableCell head scope="row">Initech</MdTableCell><MdTableCell numeric>$860.00</MdTableCell><MdTableCell>Paid</MdTableCell></MdTableRow>
<MdTableRow value="c4"><MdTableCell head scope="row">Umbrella</MdTableCell><MdTableCell numeric>$2,450.00</MdTableCell><MdTableCell>Draft</MdTableCell></MdTableRow>
<MdTableRow value="c5"><MdTableCell head scope="row">Soylent</MdTableCell><MdTableCell numeric>$1,975.00</MdTableCell><MdTableCell>Paid</MdTableCell></MdTableRow>
<MdTableRow value="c6"><MdTableCell head scope="row">Hooli</MdTableCell><MdTableCell numeric>$4,300.00</MdTableCell><MdTableCell>Pending</MdTableCell></MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<style>
.parted::part(caption) {
color: var(--md-sys-color-primary);
font-weight: 600;
letter-spacing: .06em;
text-transform: uppercase;
}
.parted::part(header-grid) { box-shadow: 0 2px 0 var(--md-sys-color-primary); }
.parted::part(grid) { background: var(--md-sys-color-surface-container-lowest); }
.parted::part(vscrollbar-thumb) { background: var(--md-sys-color-primary); }
</style>
<md-table-container variant="outlined" max-height="240px">
<md-table class="parted" frozen-header caption="Q3 invoices" column-template="2fr 1fr 1fr" label="Themed with CSS parts">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="c1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="c2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
<md-table-row value="c3"><md-table-cell head scope="row">Initech</md-table-cell><md-table-cell numeric>$860.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="c4"><md-table-cell head scope="row">Umbrella</md-table-cell><md-table-cell numeric>$2,450.00</md-table-cell><md-table-cell>Draft</md-table-cell></md-table-row>
<md-table-row value="c5"><md-table-cell head scope="row">Soylent</md-table-cell><md-table-cell numeric>$1,975.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="c6"><md-table-cell head scope="row">Hooli</md-table-cell><md-table-cell numeric>$4,300.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container><script setup>
import '@awc-ui/core/define';
</script>
<template>
<style>
.parted::part(caption) {
color: var(--md-sys-color-primary);
font-weight: 600;
letter-spacing: .06em;
text-transform: uppercase;
}
.parted::part(header-grid) { box-shadow: 0 2px 0 var(--md-sys-color-primary); }
.parted::part(grid) { background: var(--md-sys-color-surface-container-lowest); }
.parted::part(vscrollbar-thumb) { background: var(--md-sys-color-primary); }
</style>
<md-table-container variant="outlined" max-height="240px">
<md-table class="parted" frozen-header caption="Q3 invoices" column-template="2fr 1fr 1fr" label="Themed with CSS parts">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="c1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="c2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
<md-table-row value="c3"><md-table-cell head scope="row">Initech</md-table-cell><md-table-cell numeric>$860.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="c4"><md-table-cell head scope="row">Umbrella</md-table-cell><md-table-cell numeric>$2,450.00</md-table-cell><md-table-cell>Draft</md-table-cell></md-table-row>
<md-table-row value="c5"><md-table-cell head scope="row">Soylent</md-table-cell><md-table-cell numeric>$1,975.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="c6"><md-table-cell head scope="row">Hooli</md-table-cell><md-table-cell numeric>$4,300.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>
</template><script>
import '@awc-ui/core/define';
</script>
<style>
.parted::part(caption) {
color: var(--md-sys-color-primary);
font-weight: 600;
letter-spacing: .06em;
text-transform: uppercase;
}
.parted::part(header-grid) { box-shadow: 0 2px 0 var(--md-sys-color-primary); }
.parted::part(grid) { background: var(--md-sys-color-surface-container-lowest); }
.parted::part(vscrollbar-thumb) { background: var(--md-sys-color-primary); }
</style>
<md-table-container variant="outlined" max-height="240px">
<md-table class="parted" frozen-header caption="Q3 invoices" column-template="2fr 1fr 1fr" label="Themed with CSS parts">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="c1"><md-table-cell head scope="row">Acme Corp</md-table-cell><md-table-cell numeric>$1,200.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="c2"><md-table-cell head scope="row">Globex</md-table-cell><md-table-cell numeric>$3,120.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
<md-table-row value="c3"><md-table-cell head scope="row">Initech</md-table-cell><md-table-cell numeric>$860.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="c4"><md-table-cell head scope="row">Umbrella</md-table-cell><md-table-cell numeric>$2,450.00</md-table-cell><md-table-cell>Draft</md-table-cell></md-table-row>
<md-table-row value="c5"><md-table-cell head scope="row">Soylent</md-table-cell><md-table-cell numeric>$1,975.00</md-table-cell><md-table-cell>Paid</md-table-cell></md-table-row>
<md-table-row value="c6"><md-table-cell head scope="row">Hooli</md-table-cell><md-table-cell numeric>$4,300.00</md-table-cell><md-table-cell>Pending</md-table-cell></md-table-row>
</md-table-body>
</md-table>
</md-table-container>md-table::part(header-grid) { box-shadow: 0 1px 0 var(--md-sys-color-outline-variant);}A table is composed from ten elements. Each one is documented here rather than on its own page: none of them mean anything outside a table, and the parent writes several of their attributes for you.
Here they are together — the container’s top and bottom bands holding a
toolbar and a pagination bar outside the scroll region, head / body / foot
rowgroups, and a row’s expanded slot opening a detail panel underneath it:
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined" max-height="300px">
<md-table-toolbar slot="top" headline="Invoices" supporting-text="Expand a row for its detail"></md-table-toolbar>
<md-table column-template="auto 2fr 1fr 1fr" sticky-footer label="Sub-components together">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox"></md-table-cell>
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="s1" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Acme Corp"></md-table-expand-toggle></md-table-cell>
<md-table-cell head scope="row">Acme Corp</md-table-cell>
<md-table-cell numeric>$1,200.00</md-table-cell>
<md-table-cell>Paid</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Three line items · paid by card ending 4242 on 4 May.</div>
</md-table-row>
<md-table-row value="s2" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Globex"></md-table-expand-toggle></md-table-cell>
<md-table-cell head scope="row">Globex</md-table-cell>
<md-table-cell numeric>$3,120.00</md-table-cell>
<md-table-cell>Pending</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Awaiting PO approval — chased 2 days ago.</div>
</md-table-row>
<md-table-row value="s3">
<md-table-cell padding="checkbox"></md-table-cell>
<md-table-cell head scope="row">Initech</md-table-cell>
<md-table-cell numeric>$860.00</md-table-cell>
<md-table-cell>Paid</md-table-cell>
</md-table-row>
</md-table-body>
<md-table-foot>
<md-table-row rowgroup="foot" selectable="false">
<md-table-cell padding="checkbox"></md-table-cell>
<md-table-cell head scope="row">Total</md-table-cell>
<md-table-cell numeric>$5,180.00</md-table-cell>
<md-table-cell></md-table-cell>
</md-table-row>
</md-table-foot>
</md-table>
<md-table-pagination slot="bottom" count="3" page="0" rows-per-page="5"></md-table-pagination>
</md-table-container>import { MdTable, MdTableBody, MdTableCell, MdTableContainer, MdTableExpandToggle, MdTableFoot, MdTableHead, MdTablePagination, MdTableRow, MdTableToolbar } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdTableContainer variant="outlined" maxHeight="300px">
<MdTableToolbar slot="top" headline="Invoices" supportingText="Expand a row for its detail"></MdTableToolbar>
<MdTable columnTemplate="auto 2fr 1fr 1fr" stickyFooter label="Sub-components together">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head padding="checkbox"></MdTableCell>
<MdTableCell head scope="col">Client</MdTableCell>
<MdTableCell head scope="col" numeric>Amount</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow value="s1" expandable>
<MdTableCell padding="checkbox"><MdTableExpandToggle buttonLabel="Expand details for Acme Corp"></MdTableExpandToggle></MdTableCell>
<MdTableCell head scope="row">Acme Corp</MdTableCell>
<MdTableCell numeric>$1,200.00</MdTableCell>
<MdTableCell>Paid</MdTableCell>
<div slot="expanded" style={{ padding: '12px 16px', color: 'var(--md-sys-color-on-surface-variant)' }}>Three line items · paid by card ending 4242 on 4 May.</div>
</MdTableRow>
<MdTableRow value="s2" expandable>
<MdTableCell padding="checkbox"><MdTableExpandToggle buttonLabel="Expand details for Globex"></MdTableExpandToggle></MdTableCell>
<MdTableCell head scope="row">Globex</MdTableCell>
<MdTableCell numeric>$3,120.00</MdTableCell>
<MdTableCell>Pending</MdTableCell>
<div slot="expanded" style={{ padding: '12px 16px', color: 'var(--md-sys-color-on-surface-variant)' }}>Awaiting PO approval — chased 2 days ago.</div>
</MdTableRow>
<MdTableRow value="s3">
<MdTableCell padding="checkbox"></MdTableCell>
<MdTableCell head scope="row">Initech</MdTableCell>
<MdTableCell numeric>$860.00</MdTableCell>
<MdTableCell>Paid</MdTableCell>
</MdTableRow>
</MdTableBody>
<MdTableFoot>
<MdTableRow rowgroup="foot" selectable="false">
<MdTableCell padding="checkbox"></MdTableCell>
<MdTableCell head scope="row">Total</MdTableCell>
<MdTableCell numeric>$5,180.00</MdTableCell>
<MdTableCell></MdTableCell>
</MdTableRow>
</MdTableFoot>
</MdTable>
<MdTablePagination slot="bottom" count="3" page="0" rowsPerPage="5"></MdTablePagination>
</MdTableContainer>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-table-container variant="outlined" max-height="300px">
<md-table-toolbar slot="top" headline="Invoices" supporting-text="Expand a row for its detail"></md-table-toolbar>
<md-table column-template="auto 2fr 1fr 1fr" sticky-footer label="Sub-components together">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox"></md-table-cell>
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="s1" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Acme Corp"></md-table-expand-toggle></md-table-cell>
<md-table-cell head scope="row">Acme Corp</md-table-cell>
<md-table-cell numeric>$1,200.00</md-table-cell>
<md-table-cell>Paid</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Three line items · paid by card ending 4242 on 4 May.</div>
</md-table-row>
<md-table-row value="s2" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Globex"></md-table-expand-toggle></md-table-cell>
<md-table-cell head scope="row">Globex</md-table-cell>
<md-table-cell numeric>$3,120.00</md-table-cell>
<md-table-cell>Pending</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Awaiting PO approval — chased 2 days ago.</div>
</md-table-row>
<md-table-row value="s3">
<md-table-cell padding="checkbox"></md-table-cell>
<md-table-cell head scope="row">Initech</md-table-cell>
<md-table-cell numeric>$860.00</md-table-cell>
<md-table-cell>Paid</md-table-cell>
</md-table-row>
</md-table-body>
<md-table-foot>
<md-table-row rowgroup="foot" selectable="false">
<md-table-cell padding="checkbox"></md-table-cell>
<md-table-cell head scope="row">Total</md-table-cell>
<md-table-cell numeric>$5,180.00</md-table-cell>
<md-table-cell></md-table-cell>
</md-table-row>
</md-table-foot>
</md-table>
<md-table-pagination slot="bottom" count="3" page="0" rows-per-page="5"></md-table-pagination>
</md-table-container><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-table-container variant="outlined" max-height="300px">
<md-table-toolbar slot="top" headline="Invoices" supporting-text="Expand a row for its detail"></md-table-toolbar>
<md-table column-template="auto 2fr 1fr 1fr" sticky-footer label="Sub-components together">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox"></md-table-cell>
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="s1" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Acme Corp"></md-table-expand-toggle></md-table-cell>
<md-table-cell head scope="row">Acme Corp</md-table-cell>
<md-table-cell numeric>$1,200.00</md-table-cell>
<md-table-cell>Paid</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Three line items · paid by card ending 4242 on 4 May.</div>
</md-table-row>
<md-table-row value="s2" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Globex"></md-table-expand-toggle></md-table-cell>
<md-table-cell head scope="row">Globex</md-table-cell>
<md-table-cell numeric>$3,120.00</md-table-cell>
<md-table-cell>Pending</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Awaiting PO approval — chased 2 days ago.</div>
</md-table-row>
<md-table-row value="s3">
<md-table-cell padding="checkbox"></md-table-cell>
<md-table-cell head scope="row">Initech</md-table-cell>
<md-table-cell numeric>$860.00</md-table-cell>
<md-table-cell>Paid</md-table-cell>
</md-table-row>
</md-table-body>
<md-table-foot>
<md-table-row rowgroup="foot" selectable="false">
<md-table-cell padding="checkbox"></md-table-cell>
<md-table-cell head scope="row">Total</md-table-cell>
<md-table-cell numeric>$5,180.00</md-table-cell>
<md-table-cell></md-table-cell>
</md-table-row>
</md-table-foot>
</md-table>
<md-table-pagination slot="bottom" count="3" page="0" rows-per-page="5"></md-table-pagination>
</md-table-container>
</template><script>
import '@awc-ui/core/define';
</script>
<md-table-container variant="outlined" max-height="300px">
<md-table-toolbar slot="top" headline="Invoices" supporting-text="Expand a row for its detail"></md-table-toolbar>
<md-table column-template="auto 2fr 1fr 1fr" sticky-footer label="Sub-components together">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head padding="checkbox"></md-table-cell>
<md-table-cell head scope="col">Client</md-table-cell>
<md-table-cell head scope="col" numeric>Amount</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="s1" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Acme Corp"></md-table-expand-toggle></md-table-cell>
<md-table-cell head scope="row">Acme Corp</md-table-cell>
<md-table-cell numeric>$1,200.00</md-table-cell>
<md-table-cell>Paid</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Three line items · paid by card ending 4242 on 4 May.</div>
</md-table-row>
<md-table-row value="s2" expandable>
<md-table-cell padding="checkbox"><md-table-expand-toggle button-label="Expand details for Globex"></md-table-expand-toggle></md-table-cell>
<md-table-cell head scope="row">Globex</md-table-cell>
<md-table-cell numeric>$3,120.00</md-table-cell>
<md-table-cell>Pending</md-table-cell>
<div slot="expanded" style="padding: 12px 16px; color: var(--md-sys-color-on-surface-variant);">Awaiting PO approval — chased 2 days ago.</div>
</md-table-row>
<md-table-row value="s3">
<md-table-cell padding="checkbox"></md-table-cell>
<md-table-cell head scope="row">Initech</md-table-cell>
<md-table-cell numeric>$860.00</md-table-cell>
<md-table-cell>Paid</md-table-cell>
</md-table-row>
</md-table-body>
<md-table-foot>
<md-table-row rowgroup="foot" selectable="false">
<md-table-cell padding="checkbox"></md-table-cell>
<md-table-cell head scope="row">Total</md-table-cell>
<md-table-cell numeric>$5,180.00</md-table-cell>
<md-table-cell></md-table-cell>
</md-table-row>
</md-table-foot>
</md-table>
<md-table-pagination slot="bottom" count="3" page="0" rows-per-page="5"></md-table-pagination>
</md-table-container>md-table-containerThe surface around a md-table. It provides
elevation, shape, the scroll region, and the top / bottom bands where the
toolbar and pagination live.
top and bottom sit OUTSIDE the scroll region. That is exactly why the
toolbar and pagination belong there — they stay put while the rows scroll. Put
them in the default slot and they scroll away with the table.max-height is what makes the scroll region finite, and therefore what
makes sticky-header / frozen-header on md-table work at all. Without it
the container grows and the page scrolls instead — nothing to stick within.
min-height is the opposite lever: keep an empty state visible.density prop. Density belongs on md-table —
and there it is dual-typed (words or numbers).sticky-header,
frozen-header and sticky-footer are all md-table props.variant defaults to elevated. elevation (0–5) overrides the shadow level
implied by the variant — leave it unset unless you need a specific step.vibrant tints the whole table with primary / tertiary container tones and
cascades into the contained md-table via custom properties.| Property | Attribute | Type | Default | Reflects |
|---|---|---|---|---|
variant | variant | 'elevated' | 'outlined' | 'filled' | 'flat' | 'elevated' | Yes |
shape | shape | 'extra-large' | 'large' | 'medium' | 'small' | 'none' | 'large' | Yes |
maxHeight | max-height | string | '' | — |
minHeight | min-height | string | '' | — |
vibrant | vibrant | boolean | false | Yes |
elevation | elevation | 0 | 1 | 2 | 3 | 4 | 5 | — | Yes |
| Slot | Description |
|---|---|
(default) | — |
top | — |
bottom | — |
Override on the host element for per-instance theming:
| Property | Description |
|---|---|
--md-table-container-color | Surface background |
--md-table-container-shape | Border-radius |
--md-table-container-elevation | Box-shadow |
--md-table-container-outline-color | Outline color (variant=outlined) |
--md-table-container-outline-width | Outline width (variant=outlined) |
Style internal elements through shadow DOM with ::part():
| Part | Description |
|---|---|
top | Wrapper around `slot[name="top"]` (toolbar etc.) |
scroll | The scroll viewport that contains the table |
bottom | Wrapper around `slot[name="bottom"]` (pagination etc.) |
md-table-headThe column-header rowgroup — <thead> for md-table.
Purely presentational: it marks the region, applies the head background, and
lets its rows flow into the table’s grid.
display: contents, so its rows land directly on the
table’s grid tracks. It is not a box — you cannot pad it, border it, give
it a height, or scroll it. Style the cells instead.rowgroup="head" on each row, and head + scope="col" on
each cell. The wrapper does not cascade anything to its descendants.md-table, not from here: use its
sticky-header (pinned inside the scroll region) or frozen-header (lifted
out of it), paired with max-height on
md-table-container. Never set
position: sticky on this element.slotchange as a bubbling event so md-table notices
row reorders (slotchange isn’t composed). That’s plumbing — don’t listen to
it.| Slot | Description |
|---|---|
(default) | — |
md-table-bodyThe data rowgroup — <tbody> for md-table. Purely
presentational; its rows flow into the table’s grid.
display: contents, so rows land on the table’s tracks.
It is not a box: don’t size it, pad it, border it or give it
overflow: auto. Scrolling belongs to
md-table-container via its max-height.rowgroup. rowgroup="body" is the default on
md-table-row; the wrapper doesn’t stamp it, and it
certainly doesn’t make cells into headers.md-table-body elements in one table are allowed — useful for
visually grouped sections — but each is announced as a rowgroup unless you
set presentational.presentational drops the rowgroup role. Use it only when the grouping is
decorative; leave it off for the normal single-body case.md-table props (sticky-header,
frozen-header, sticky-footer) — never something you configure here.| Property | Attribute | Type | Default | Reflects |
|---|---|---|---|---|
presentational | presentational | boolean | false | Yes |
| Slot | Description |
|---|---|
(default) | — |
md-table-footThe summary rowgroup — <tfoot> for md-table. Where
totals and aggregate rows live. Purely presentational; its rows flow into the
table’s grid.
display: contents, so the rows land on the table’s
tracks. It is not a box — no padding, borders, height or scrolling on this
element. Style the cells.rowgroup="foot" on its rows. The wrapper doesn’t stamp it, and
body rows in the foot get the wrong styling and the wrong semantics.selectable="false" on totals rows, so selectAll() on
md-table doesn’t sweep the summary row up with the
data.md-table’s sticky-footer, not from here —
paired with max-height on
md-table-container. Never set
position: sticky on this element.presentational drops the rowgroup role for a purely decorative grouping;
leave it off for a real summary group.| Property | Attribute | Type | Default | Reflects |
|---|---|---|---|---|
presentational | presentational | boolean | false | Yes |
| Slot | Description |
|---|---|
(default) | — |
md-table-rowOne row of a md-table. A real grid box that spans
the table’s tracks and lays out its md-table-cell
children, with selection, click and expansion state.
value is the row’s identity in the table’s selection state. A body row
without a value cannot be tracked by md-table’s getSelection().selectable defaults to true, not false. Aggregate and totals rows must
set selectable="false" or selectAll() picks them up as data.rowgroup must match the wrapping group — rowgroup="head" inside
md-table-head, "foot" inside md-table-foot. The default is body, so a
header row left untouched gets body styling and semantics.display: contents. Only cells and the expanded slot
belong in it — a wrapper <div> around the cells breaks the layout.clickable and selection are independent: a row can be clickable without
being selectable, and vice versa. Set each explicitly.expanded slot, not in a second injected row.| Property | Attribute | Type | Default | Reflects |
|---|---|---|---|---|
rowgroup | rowgroup | 'head' | 'body' | 'foot' | 'body' | Yes |
selected | selected | boolean | false | Yes |
disabled | disabled | boolean | false | Yes |
selectable | selectable | boolean | true | — |
clickable | clickable | boolean | false | Yes |
value | value | string | '' | — |
expandable | expandable | boolean | false | Yes |
expanded | expanded | boolean | false | Yes |
highlight | highlight | boolean | false | Yes |
| Method | Parameters |
|---|---|
toggle() | none |
| Slot | Description |
|---|---|
(default) | — |
expanded | — |
Override on the host element for per-instance theming:
| Property | Description |
|---|---|
--md-table-row-hover-color | — |
Style internal elements through shadow DOM with ::part():
| Part | Description |
|---|---|
expanded | — |
md-table-cellOne cell of a md-table. Renders as a data cell, a
column header or a row header depending on head and scope, with alignment,
spanning, sticky pinning and truncation options.
head and scope together are what make a header. head alone only
styles the cell; scope is what associates it with its column or row for
assistive tech. Set both — scope="col" on column headers, scope="row" on
the identifying first column.numeric is not just alignment — it applies tabular figures so digits line
up across rows. Use it on every numeric column.sticky cell needs an opaque background or the scrolling rows bleed
through it. That’s --md-table-cell-sticky-bg. The table’s pin-mode and
pinColumn() coordinate multi-column pinning.colspan / rowspan use the HTML attribute casing — colspan="2", not
col-span="2" (the properties are colSpan / rowSpan).ellipsis truncates instead of wrapping, so the full text must be
reachable another way — a md-tooltip or the row’s
expanded slot. Truncation on its own loses information.padding="checkbox" is the tightened padding for a selection column;
padding="none" is for edge-to-edge content like an image.variant="meta" is the built-in secondary/meta text style — don’t shrink text
with custom CSS.| Property | Attribute | Type | Default | Reflects |
|---|---|---|---|---|
align | align | 'start' | 'center' | 'end' | 'justify' | 'start' | Yes |
numeric | numeric | boolean | false | Yes |
padding | padding | 'default' | 'none' | 'checkbox' | 'default' | Yes |
head | head | boolean | false | Yes |
scope | scope | 'col' | 'row' | 'colgroup' | 'rowgroup' | '' | '' | Yes |
sticky | sticky | 'start' | 'end' | '' | '' | Yes |
colSpan | colspan | number | 1 | — |
rowSpan | rowspan | number | 1 | — |
ellipsis | ellipsis | boolean | false | Yes |
variant | variant | 'body' | 'meta' | 'body' | Yes |
noPinIndicator | no-pin-indicator | boolean | false | Yes |
pinIcon | pin-icon | string | '' | — |
density | density | 0 | -1 | -2 | -3 | -4 | 0 | Yes |
| Slot | Description |
|---|---|
(default) | — |
Override on the host element for per-instance theming:
| Property | Description |
|---|---|
--md-table-cell-color | Foreground (text) color |
--md-table-cell-background | Background color |
--md-table-cell-padding-inline | Horizontal padding |
--md-table-cell-min-height | Minimum cell height |
--md-table-cell-sticky-bg | Opaque bg for sticky columns |
--md-table-cell-padding-block | — |
--md-table-cell-pin-display | — |
--md-table-cell-pin-color | — |
--md-table-cell-pin-size | — |
--md-table-cell-sticky-transition | — |
Style internal elements through shadow DOM with ::part():
| Part | Description |
|---|---|
pin | — |
content | — |
md-table-toolbarThe band above a md-table. Title and supporting text
in the resting state; a selection count and bulk actions when rows are selected.
num-selected > 0 switches the toolbar into selection mode: the headline
is replaced by the selection caption, and selection-actions replace
actions. A stale count leaves it stuck in (or out of) selection mode.auto-bind makes the toolbar find the sibling table and track its selection
itself — no listener needed. It only works when the toolbar and the table are
inside the same md-table-container.label-selected uses the %count% token, not {count}. This is a
different token style from the rest of the library —
md-transfer-list and
md-time-picker use braces. Keep %count% when
translating.selection-actions; putting them in actions shows
them when nothing is selected.label or caption on
md-table as well — the headline is visual only.compact is the built-in tighter band; don’t shrink it with custom CSS.| Property | Attribute | Type | Default | Reflects |
|---|---|---|---|---|
headline | headline | string | '' | — |
supportingText | supporting-text | string | '' | — |
numSelected | num-selected | number | 0 | Yes |
autoBind | auto-bind | boolean | false | — |
labelSelected | label-selected | string | '%count% selected' | — |
compact | compact | boolean | false | Yes |
density | density | 0 | -1 | -2 | -3 | -4 | 0 | Yes |
| Slot | Description |
|---|---|
leading | — |
headline | — |
supporting-text | — |
title-area | — |
selection-actions | — |
actions | — |
Override on the host element for per-instance theming:
| Property | Description |
|---|---|
--md-table-toolbar-color | Foreground color |
--md-table-toolbar-bg | Background color (default mode) |
--md-table-toolbar-selection-bg | Background color (selection mode) |
--md-table-toolbar-selection-color | Foreground color (selection mode) |
--md-table-toolbar-padding-block | Top/bottom padding |
--md-table-toolbar-padding-inline | Left/right padding |
--md-table-toolbar-min-height | Minimum height (default: 64px) |
Style internal elements through shadow DOM with ::part():
| Part | Description |
|---|---|
leading | Container for the `leading` slot |
main | Container for headline / supporting text |
selection-caption | "%count% selected" caption (selection mode) |
headline | <h2> element |
supporting-text | <p> element |
actions | — |
md-table-paginationThe page controls under a md-table. A rows-per-page
selector, a “1–10 of 120” readout, and previous/next (optionally first/last)
navigation.
page is zero-based. “Page 1” in the readout is page="0". Treating it as
one-based puts an off-by-one in every fetch.mdPageChange and
mdRowsPerPageChange; you re-render the rows and update page, count and
rows-per-page yourself.rows-per-page-options is a comma-separated string, not an array —
rows-per-page-options="10,25,50", never el.rowsPerPageOptions = [10, 25, 50].
It is parsed with .split(','), so it is string-only in both the attribute and
the property form — unlike md-color-picker’s presets, which is typed
string | string[] and does accept an array via the property. "all" is a
legal entry and maps to the full count.label-displayed-rows uses %from%, %to% and %count% — percent
tokens, matching md-table-toolbar and unlike
the {…} tokens used by md-transfer-list and md-time-picker. Keep all
three when translating.page to 0 — that’s your call,
not the component’s, or the user can land on page 12 of 3.disabled while a page is loading so users can’t queue conflicting page
changes.| Property | Attribute | Type | Default | Reflects |
|---|---|---|---|---|
count | count | number | 0 | — |
page | page | number | 0 | Yes |
rowsPerPage | rows-per-page | number | 10 | Yes |
rowsPerPageOptions | rows-per-page-options | string | '5,10,25,50' | — |
showFirstLast | show-first-last | boolean | false | Yes |
labelDisplayedRows | label-displayed-rows | string | '%from%–%to% of %count%' | — |
labelRowsPerPage | label-rows-per-page | string | 'Rows per page:' | — |
labelFirstPage | label-first-page | string | 'First page' | — |
labelPreviousPage | label-previous-page | string | 'Previous page' | — |
labelNextPage | label-next-page | string | 'Next page' | — |
labelLastPage | label-last-page | string | 'Last page' | — |
labelAll | label-all | string | 'All' | — |
compact | compact | boolean | false | Yes |
disabled | disabled | boolean | false | Yes |
density | density | 0 | -1 | -2 | -3 | -4 | 0 | Yes |
| Method | Parameters |
|---|---|
goToPage() | page: number |
setRowsPerPage() | rpp: number |
| Slot | Description |
|---|---|
actions | — |
Override on the host element for per-instance theming:
| Property | Description |
|---|---|
--md-table-pagination-color | Foreground color |
--md-table-pagination-bg | Background color |
--md-table-pagination-padding-block | Top/bottom padding |
--md-table-pagination-padding-inline | Left/right padding |
--md-table-pagination-select-width | Rows-per-page select width (default 72px) |
Style internal elements through shadow DOM with ::part():
| Part | Description |
|---|---|
display | The "from–to of count" label |
rpp | Rows-per-page region |
rpp-select | The md-select picker |
nav | Navigation buttons region |
first-button | — |
prev-button | — |
next-button | — |
last-button | — |
md-table-sort-labelThe sortable-column affordance for md-table. It goes
inside a header cell, shows the sort direction, and emits a request to sort —
it does not sort anything itself.
mdSortRequest tells you which column was
clicked and its defaultOrder; you decide the next order, re-sort your rows,
and reflect the result.active and order are display state you own. Nothing updates them
automatically — leave them stale and the arrow lies about the current sort.active at a time. Two active indicators make the
sort state ambiguous.default-order is the direction used on the first click of this column;
desc is usually right for dates and amounts.column is the key echoed back in the event — keep it matching your data field
and untranslated. Never use the label text as the key.md-table’s animateNextChange() before re-rendering or you lose the
reorder animation.disabled keeps the header readable while removing the affordance.| Property | Attribute | Type | Default | Reflects |
|---|---|---|---|---|
column | column | string | '' | — |
disabled | disabled | boolean | false | Yes |
defaultOrder | default-order | 'asc' | 'desc' | 'asc' | — |
iconPosition | icon-position | 'start' | 'end' | 'end' | Yes |
icon | icon | string | '' | — |
inactiveIcon | inactive-icon | 'always' | 'hover' | 'always' | Yes |
active | active | boolean | false | Yes |
order | order | 'asc' | 'desc' | 'none' | 'none' | Yes |
density | density | 0 | -1 | -2 | -3 | -4 | 0 | Yes |
| Slot | Description |
|---|---|
(default) | — |
Override on the host element for per-instance theming:
| Property | Description |
|---|---|
--md-table-sort-label-color | Inactive color |
--md-table-sort-label-active-color | Active color |
--md-table-sort-label-icon-size | Icon size (default 18px) |
--md-table-sort-label-gap | Gap between text and icon |
--md-table-sort-label-rest-opacity | — |
Style internal elements through shadow DOM with ::part():
| Part | Description |
|---|---|
icon | The arrow indicator |
label | The text label |
md-table-expand-toggleThe chevron that expands a md-table row. A tiny
wrapper around md-icon-button that rotates with the
row’s expanded state.
md-table-row’s expanded drives the
rotation, and the row is what emits mdRowExpandedChange. Never try to read
expanded state off the toggle.button-label defaults to the English "Expand row" and is the toggle’s
only accessible name. In a table of twenty rows it is also ambiguous — make it
row-specific (“Expand invoice for Acme”).chevron_right; a symmetric glyph like add looks broken when
rotated.density prop — it follows the cell it sits in.padding="checkbox" so a small control isn’t ringed by full cell
padding.| Property | Attribute | Type | Default | Reflects |
|---|---|---|---|---|
buttonLabel | button-label | string | 'Expand row' | — |
icon | icon | string | 'chevron_right' | — |
Override on the host element for per-instance theming:
| Property | Description |
|---|---|
--md-table-expand-toggle-rotation | Expanded rotation (default 90deg) |
md-table-container ·
md-table-head ·
md-table-body ·
md-table-foot ·
md-table-row ·
md-table-cell ·
md-table-toolbar ·
md-table-pagination ·
md-table-sort-label ·
md-table-expand-toggle ·
md-list
md-tableTwo artefacts to give your AI agent so it generates correct UI with this component. The per-component spec answers "how do I use this exact tag?". The main-llm spec answers "which tag should I pick in the first place?".
md-table spec card
Identity · when to use / when NOT · decision cues · behavioural contract · do/don't · anti-patterns · full API. Paste into your agent when you're implementing with this component.
System-prompt preamble · decision matrix · token reference · page recipes · anti-patterns. Paste into the system prompt at the start of a piece of work.