Table
A styled HTML table in parts: header, body, footer, row, heading cell, cell and caption.
Install
If this is the first component in the project, set up once. This writes ermine.json and copies the theme stylesheet. The installation guide covers Vue, Nuxt and Astro.
npx ermineui initpnpm dlx ermineui inityarn dlx ermineui initbunx ermineui initThen add the component:
npx ermineui add tablepnpm dlx ermineui add tableyarn dlx ermineui add tablebunx ermineui add tableThat copies 13 files into src/components/ui/table/. Files you already have are kept.
Use
import {
Table,
TableBody,
TableCaption,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table'<script setup lang="ts">
import { Table } from '@/components/ui/table'
</script>
<template>
<Table />
</template>In Nuxt, with the auto-import setting, the tags work in templates without the import. In Astro, use it inside a Vue island. The props are documented in the source below, on each one.
What gets copied
| File | Where it goes |
|---|---|
Table.vue | src/components/ui/table/Table.vue |
TableBody tbody sb-table-body.vue | src/components/ui/table/TableBody tbody sb-table-body.vue |
TableBody.vue | src/components/ui/table/TableBody.vue |
TableCaption caption sb-table-caption.vue | src/components/ui/table/TableCaption caption sb-table-caption.vue |
TableCaption.vue | src/components/ui/table/TableCaption.vue |
TableCell.vue | src/components/ui/table/TableCell.vue |
TableFooter tfoot sb-table-footer.vue | src/components/ui/table/TableFooter tfoot sb-table-footer.vue |
TableFooter.vue | src/components/ui/table/TableFooter.vue |
TableHead.vue | src/components/ui/table/TableHead.vue |
TableHeader thead sb-table-header.vue | src/components/ui/table/TableHeader thead sb-table-header.vue |
TableHeader.vue | src/components/ui/table/TableHeader.vue |
TableRow.vue | src/components/ui/table/TableRow.vue |
index.ts | src/components/ui/table/index.ts |
The folders are the suggested ones. ermine.json says where yours are.
Source
This is exactly what the command copies. To install by hand, create each file below and the helpers listed above.
Table.vue 89 lines
<script setup lang="ts">
defineOptions({ inheritAttrs: false })
</script>
<template>
<!-- The wrapper scrolls sideways, so a wide table never pushes the page wider on a phone. -->
<div class="er-table-wrap">
<table v-bind="$attrs" class="er-table"><slot /></table>
</div>
</template>
<style>
@layer components {
.er-table-wrap {
/* Positioned, so anything visually hidden inside scrolls with the table instead of widening the page. */
position: relative;
width: 100%;
overflow-x: auto;
}
.er-table {
width: 100%;
border-collapse: collapse;
font-size: 0.875rem;
caption-side: bottom;
}
.er-table-caption {
margin-top: 0.75rem;
color: var(--er-muted-foreground);
font-size: 0.8125rem;
}
.er-table-row {
border-bottom: 1px solid var(--er-border);
transition: background-color var(--er-duration);
}
.er-table-body .er-table-row:last-child {
border-bottom: 0;
}
.er-table-body .er-table-row:hover {
background: color-mix(in oklab, var(--er-muted) 50%, transparent);
}
.er-table-row[data-state='selected'],
.er-table-body .er-table-row[data-state='selected']:hover {
background: var(--er-muted);
}
.er-table-head,
.er-table-cell {
padding: 0.625rem 0.75rem;
text-align: left;
vertical-align: middle;
}
.er-table-head {
height: 2.5rem;
padding-block: 0;
color: var(--er-muted-foreground);
font-weight: var(--er-weight-medium);
white-space: nowrap;
}
.er-table-head[data-align='right'],
.er-table-cell[data-align='right'] {
text-align: right;
font-variant-numeric: tabular-nums;
}
.er-table-head[data-align='center'],
.er-table-cell[data-align='center'] {
text-align: center;
}
.er-table-footer {
border-top: 1px solid var(--er-border);
background: color-mix(in oklab, var(--er-muted) 50%, transparent);
font-weight: var(--er-weight-medium);
}
.er-table-footer .er-table-row {
border-bottom: 0;
}
}
</style>TableBody tbody sb-table-body.vue 4 lines
<template>
< class=""><slot /></>
</template>TableBody.vue 4 lines
<template>
<tbody class="er-table-body"><slot /></tbody>
</template>TableCaption caption sb-table-caption.vue 4 lines
<template>
< class=""><slot /></>
</template>TableCaption.vue 4 lines
<template>
<caption class="er-table-caption"><slot /></caption>
</template>TableCell.vue 14 lines
<script setup lang="ts">
withDefaults(
defineProps<{
/** Use "right" for numbers, so the digits line up. */
align?: 'left' | 'center' | 'right'
}>(),
{ align: 'left' },
)
</script>
<template>
<td class="er-table-cell" :data-align="align"><slot /></td>
</template>TableFooter tfoot sb-table-footer.vue 4 lines
<template>
< class=""><slot /></>
</template>TableFooter.vue 4 lines
<template>
<tfoot class="er-table-footer"><slot /></tfoot>
</template>TableHead.vue 16 lines
<script setup lang="ts">
withDefaults(
defineProps<{
/** Use "right" for columns of numbers, so the digits line up. */
align?: 'left' | 'center' | 'right'
/** "col" for a column heading (the default), "row" for a heading at the start of a row. */
scope?: 'col' | 'row'
}>(),
{ align: 'left', scope: 'col' },
)
</script>
<template>
<th class="er-table-head" :scope="scope" :data-align="align"><slot /></th>
</template>TableHeader thead sb-table-header.vue 4 lines
<template>
< class=""><slot /></>
</template>TableHeader.vue 4 lines
<template>
<thead class="er-table-header"><slot /></thead>
</template>TableRow.vue 11 lines
<script setup lang="ts">
defineProps<{
/** Highlight the row, e.g. while its checkbox is ticked. */
selected?: boolean
}>()
</script>
<template>
<tr class="er-table-row" :data-state="selected ? 'selected' : undefined" :aria-selected="selected || undefined"><slot /></tr>
</template>index.ts 10 lines
/** Table — a styled HTML table in parts: header, body, footer, row, heading cell, cell and caption. */
export { default as Table } from './Table.vue'
export { default as TableBody } from './TableBody.vue'
export { default as TableCaption } from './TableCaption.vue'
export { default as TableCell } from './TableCell.vue'
export { default as TableFooter } from './TableFooter.vue'
export { default as TableHead } from './TableHead.vue'
export { default as TableHeader } from './TableHeader.vue'
export { default as TableRow } from './TableRow.vue'