Table
Tables are used to display data in a structured and organized manner. It is the most common way to display a lot of structured data in a web page. Since we use the <table>
tag, it is accessible to the screen reader without any modification.
Simple Table
A simple table with a header and a body.
Product | Color | Type | Stock |
---|---|---|---|
T-Shirt | Blue | Tops | 15 |
Jeans | Black | Bottoms | 32 |
Sneakers | White | Shoes | 23 |
Dress | Red | Dresses | 11 |
<div class="m-2 overflow-x-auto shadow-md"> <table class="w-full rounded-lg text-left text-base text-gray-800 shadow-md dark:bg-black dark:text-gray-300" > <thead class="bg-gray-200 text-sm uppercase text-gray-800 dark:bg-zinc-800 dark:text-gray-300" > <tr> <th scope="col" class="px-6 py-3">Product</th> <th scope="col" class="px-6 py-3">Color</th> <th scope="col" class="px-6 py-3">Type</th> <th scope="col" class="px-6 py-3">Stock</th> </tr> </thead> <tbody class="divide-y divide-gray-300 dark:divide-zinc-800"> <tr class="bg-white dark:bg-zinc-950"> <th scope="row" class="whitespace-nowrap px-6 py-3 font-semibold text-gray-900 dark:text-gray-100" > T-Shirt </th> <td class="px-6 py-3">Blue</td> <td class="px-6 py-3">Tops</td> <td class="px-6 py-3">15</td> </tr> <tr class="bg-gray-100 dark:bg-zinc-900"> <th scope="row" class="whitespace-nowrap px-6 py-3 font-semibold text-gray-900 dark:text-gray-100" > Jeans </th> <td class="px-6 py-3">Black</td> <td class="px-6 py-3">Bottoms</td> <td class="px-6 py-3">32</td> </tr> <tr class="bg-white dark:bg-zinc-950"> <th scope="row" class="whitespace-nowrap px-6 py-3 font-semibold text-gray-900 dark:text-gray-100" > Sneakers </th> <td class="px-6 py-3">White</td> <td class="px-6 py-3">Shoes</td> <td class="px-6 py-3">23</td> </tr> <tr class="bg-gray-100 dark:bg-zinc-900"> <th scope="row" class="whitespace-nowrap px-6 py-3 font-semibold text-gray-900 dark:text-gray-100" > Dress </th> <td class="px-6 py-3">Red</td> <td class="px-6 py-3">Dresses</td> <td class="px-6 py-3">11</td> </tr> </tbody> </table></div>
---interface TableItem { name: string; color: string; type: string; stock: string;}/* You can use this interface above to define the structure of your data and receive it as a prop */
const tableData: TableItem[] = [ { name: "T-Shirt", color: "Blue", type: "Tops", stock: "15", }, { name: "Jeans", color: "Black", type: "Bottoms", stock: "32", }, { name: "Sneakers", color: "White", type: "Shoes", stock: "23", }, { name: "Dress", color: "Red", type: "Dresses", stock: "11", },];---
{ tableData.length > 0 && ( <div class="relative m-2 overflow-x-auto rounded-lg border border-gray-300 shadow-md dark:border-zinc-800 dark:shadow-lg dark:shadow-black"> <table class="w-full rounded-lg text-left text-base text-gray-800 shadow-md dark:text-gray-300"> <thead class="bg-gray-200 text-sm uppercase text-gray-800 dark:bg-zinc-800 dark:text-gray-300"> <tr> <th scope="col" class="px-6 py-3"> Product </th> <th scope="col" class="px-6 py-3"> Color </th> <th scope="col" class="px-6 py-3"> Type </th> <th scope="col" class="px-6 py-3"> Stock </th> </tr> </thead> <tbody class="divide-y divide-gray-300 dark:divide-zinc-800"> {tableData.map((item) => ( <tr class="odd:bg-white even:bg-gray-100 odd:dark:bg-zinc-950 even:dark:bg-zinc-900"> <th scope="row" class="whitespace-nowrap px-6 py-3 font-semibold text-gray-900 dark:text-gray-100" > {item.name} </th> <td class="px-6 py-3">{item.color}</td> <td class="px-6 py-3">{item.type}</td> <td class="px-6 py-3">{item.stock}</td> </tr> ))} </tbody> </table> </div> )}
Thematic Table
A variation of the simple table with more thematic colors.
Product | Color | Type | Stock |
---|---|---|---|
T-Shirt | Blue | Tops | 15 |
Jeans | Black | Bottoms | 32 |
Sneakers | White | Shoes | 23 |
Dress | Red | Dresses | 11 |
<div class="relative m-2 overflow-x-auto rounded-lg border border-gray-300 shadow-md dark:border-zinc-800 dark:shadow-lg dark:shadow-black"> <table class="w-full overflow-hidden rounded-lg text-left text-sm text-gray-800 shadow-md dark:text-gray-300" > <thead class="bg-indigo-200 text-sm uppercase text-gray-700 dark:bg-indigo-950/70 dark:text-gray-200" > <tr class=""> <th scope="col" class="px-6 py-3">Product</th> <th scope="col" class="px-6 py-3">Color</th> <th scope="col" class="px-6 py-3">Category</th> <th scope="col" class="px-6 py-3">Price</th> </tr> </thead> <tbody class="divide-y divide-gray-300 dark:divide-zinc-800"> <tr class="bg-white dark:bg-zinc-950"> <th scope="row" class="whitespace-nowrap px-6 py-3 font-semibold text-gray-900 dark:text-gray-100" > T-Shirt </th> <td class="px-6 py-3">Blue</td> <td class="px-6 py-3">Tops</td> <td class="px-6 py-3">15</td> </tr> <tr class="bg-gray-100 dark:bg-zinc-900"> <th scope="row" class="whitespace-nowrap px-6 py-3 font-semibold text-gray-900 dark:text-gray-100" > Jeans </th> <td class="px-6 py-3">Black</td> <td class="px-6 py-3">Bottoms</td> <td class="px-6 py-3">32</td> </tr> <tr class="bg-white dark:bg-zinc-950"> <th scope="row" class="whitespace-nowrap px-6 py-3 font-semibold text-gray-900 dark:text-gray-100" > Sneakers </th> <td class="px-6 py-3">White</td> <td class="px-6 py-3">Shoes</td> <td class="px-6 py-3">23</td> </tr> <tr class="bg-gray-100 dark:bg-zinc-900"> <th scope="row" class="whitespace-nowrap px-6 py-3 font-semibold text-gray-900 dark:text-gray-100" > Dress </th> <td class="px-6 py-3">Red</td> <td class="px-6 py-3">Dresses</td> <td class="px-6 py-3">11</td> </tr> </tbody> </table></div>
---interface TableItem { name: string; color: string; type: string; stock: string;}/* You can use this interface above to define the structure of your data and receive it as a prop */
const tableData: TableItem[] = [ { name: "T-Shirt", color: "Blue", type: "Tops", stock: "15", }, { name: "Jeans", color: "Black", type: "Bottoms", stock: "32", }, { name: "Sneakers", color: "White", type: "Shoes", stock: "23", }, { name: "Dress", color: "Red", type: "Dresses", stock: "11", },];---
{ tableData.length > 0 && ( <div class="relative m-2 overflow-x-auto rounded-lg border border-gray-300 shadow-md dark:border-zinc-800 dark:shadow-lg dark:shadow-black"> <table class="w-full rounded-lg text-left text-base text-gray-800 shadow-md dark:bg-black dark:text-gray-300"> <thead class="bg-indigo-200 text-sm uppercase text-gray-700 dark:bg-indigo-950/70 dark:text-gray-200"> <tr> <th scope="col" class="px-6 py-3"> Product </th> <th scope="col" class="px-6 py-3"> Color </th> <th scope="col" class="px-6 py-3"> Type </th> <th scope="col" class="px-6 py-3"> Stock </th> </tr> </thead> <tbody class="divide-y divide-indigo-200 dark:divide-zinc-800"> {tableData.map((item) => ( <tr class="odd:bg-white even:bg-indigo-50 odd:dark:bg-zinc-950 even:dark:bg-zinc-900"> <th scope="row" class="whitespace-nowrap px-6 py-3 font-semibold text-gray-900 dark:text-gray-100" > {item.name} </th> <td class="px-6 py-3">{item.color}</td> <td class="px-6 py-3">{item.type}</td> <td class="px-6 py-3">{item.stock}</td> </tr> ))} </tbody> </table> </div> )}
Table With Cell Borders
A variation of the simple table with cell borders.
Product | Color | Type | Stock |
---|---|---|---|
T-Shirt | Blue | Tops | 15 |
Jeans | Black | Bottoms | 32 |
Sneakers | White | Shoes | 23 |
Dress | Red | Dresses | 11 |
<div class="relative m-2 overflow-x-auto rounded-lg shadow-md dark:shadow-lg dark:shadow-black"> <table class="w-full border-collapse rounded-lg border border-gray-300 text-left text-base text-gray-800 shadow-md dark:border-zinc-800 dark:bg-black dark:text-gray-300" > <thead class="bg-gray-200 text-sm uppercase text-gray-800 dark:bg-zinc-800 dark:text-gray-300" > <tr> <th scope="col" class="border border-gray-300 px-6 py-3 dark:border-zinc-700" > Product </th> <th scope="col" class="border border-gray-300 px-6 py-3 dark:border-zinc-700" > Color </th> <th scope="col" class="border border-gray-300 px-6 py-3 dark:border-zinc-700" > Type </th> <th scope="col" class="border border-gray-300 px-6 py-3 dark:border-zinc-700" > Stock </th> </tr> </thead> <tbody class="divide-y divide-gray-300 dark:divide-zinc-800"> <tr class="bg-white dark:bg-zinc-950"> <th scope="row" class="whitespace-nowrap border border-gray-300 px-6 py-3 font-semibold text-gray-900 dark:border-zinc-700 dark:text-gray-100" > T-Shirt </th> <td class="border border-gray-300 px-6 py-3 dark:border-zinc-700"> Blue </td> <td class="border border-gray-300 px-6 py-3 dark:border-zinc-700"> Tops </td> <td class="border border-gray-300 px-6 py-3 dark:border-zinc-700"> 15 </td> </tr> <tr class="bg-gray-100 dark:bg-zinc-900"> <th scope="row" class="whitespace-nowrap border border-gray-300 px-6 py-3 font-semibold text-gray-900 dark:border-zinc-700 dark:text-gray-100" > Jeans </th> <td class="border border-gray-300 px-6 py-3 dark:border-zinc-700"> Black </td> <td class="border border-gray-300 px-6 py-3 dark:border-zinc-700"> Bottoms </td> <td class="border border-gray-300 px-6 py-3 dark:border-zinc-700"> 32 </td> </tr> <tr class="bg-white dark:bg-zinc-950"> <th scope="row" class="whitespace-nowrap border border-gray-300 px-6 py-3 font-semibold text-gray-900 dark:border-zinc-700 dark:text-gray-100" > Sneakers </th> <td class="border border-gray-300 px-6 py-3 dark:border-zinc-700"> White </td> <td class="border border-gray-300 px-6 py-3 dark:border-zinc-700"> Shoes </td> <td class="border border-gray-300 px-6 py-3 dark:border-zinc-700"> 23 </td> </tr> <tr class="bg-gray-100 dark:bg-zinc-900"> <th scope="row" class="whitespace-nowrap border border-gray-300 px-6 py-3 font-semibold text-gray-900 dark:border-zinc-700 dark:text-gray-100" > Dress </th> <td class="border border-gray-300 px-6 py-3 dark:border-zinc-700"> Red </td> <td class="border border-gray-300 px-6 py-3 dark:border-zinc-700"> Dresses </td> <td class="border border-gray-300 px-6 py-3 dark:border-zinc-700"> 11 </td> </tr> </tbody> </table></div>
---interface TableItem { name: string; color: string; type: string; stock: string;}/* You can use this interface above to define the structure of your data and receive it as a prop */
const tableData: TableItem[] = [ { name: "T-Shirt", color: "Blue", type: "Tops", stock: "15", }, { name: "Jeans", color: "Black", type: "Bottoms", stock: "32", }, { name: "Sneakers", color: "White", type: "Shoes", stock: "23", }, { name: "Dress", color: "Red", type: "Dresses", stock: "11", },];---
{ tableData.length > 0 && ( <div class="relative m-2 overflow-x-auto rounded-lg shadow-md dark:shadow-lg dark:shadow-black"> <table class="w-full border-collapse rounded-lg border border-gray-300 text-left text-base text-gray-800 shadow-md dark:border-zinc-800 dark:text-gray-300"> <thead class="bg-gray-200 text-sm uppercase text-gray-800 dark:bg-zinc-800 dark:text-gray-300"> <tr> <th scope="col" class="border border-gray-300 px-6 py-3 dark:border-zinc-700" > Product </th> <th scope="col" class="border border-gray-300 px-6 py-3 dark:border-zinc-700" > Color </th> <th scope="col" class="border border-gray-300 px-6 py-3 dark:border-zinc-700" > Type </th> <th scope="col" class="border border-gray-300 px-6 py-3 dark:border-zinc-700" > Stock </th> </tr> </thead> <tbody> {tableData.map((item) => ( <tr class="odd:bg-white even:bg-gray-100 odd:dark:bg-zinc-950 even:dark:bg-zinc-900"> <th scope="row" class="whitespace-nowrap border border-gray-300 px-6 py-3 font-semibold text-gray-900 dark:border-zinc-800 dark:text-gray-100" > {item.name} </th> <td class="border border-gray-300 px-6 py-3 dark:border-zinc-800"> {item.color} </td> <td class="border border-gray-300 px-6 py-3 dark:border-zinc-800"> {item.type} </td> <td class="border border-gray-300 px-6 py-3 dark:border-zinc-800"> {item.stock} </td> </tr> ))} </tbody> </table> </div> )}
Table With A Caption
A table with a caption, done semantically with the <caption>
tag.
Product | Color | Type | Stock |
---|---|---|---|
T-Shirt | Blue | Tops | 15 |
Jeans | Black | Bottoms | 32 |
Sneakers | White | Shoes | 23 |
Dress | Red | Dresses | 11 |
<div class="relative m-2 overflow-x-auto rounded-lg border border-gray-300 shadow-md dark:border-zinc-800 dark:shadow-lg dark:shadow-black"> <table class="w-full rounded-lg text-left text-base text-gray-800 shadow-md dark:bg-black dark:text-gray-300" > <thead class="bg-gray-200 text-sm uppercase text-gray-800 dark:bg-zinc-800 dark:text-gray-300" > <tr> <th scope="col" class="px-6 py-3">Product</th> <th scope="col" class="px-6 py-3">Color</th> <th scope="col" class="px-6 py-3">Type</th> <th scope="col" class="px-6 py-3">Stock</th> </tr> </thead> <tbody class="divide-y divide-gray-300 dark:divide-zinc-800"> <tr class="bg-white dark:bg-zinc-950"> <th scope="row" class="whitespace-nowrap px-6 py-3 font-semibold text-gray-900 dark:text-gray-100" > T-Shirt </th> <td class="px-6 py-3">Blue</td> <td class="px-6 py-3">Tops</td> <td class="px-6 py-3">15</td> </tr> <tr class="bg-gray-100 dark:bg-zinc-900"> <th scope="row" class="whitespace-nowrap px-6 py-3 font-semibold text-gray-900 dark:text-gray-100" > Jeans </th> <td class="px-6 py-3">Black</td> <td class="px-6 py-3">Bottoms</td> <td class="px-6 py-3">32</td> </tr> <tr class="bg-white dark:bg-zinc-950"> <th scope="row" class="whitespace-nowrap px-6 py-3 font-semibold text-gray-900 dark:text-gray-100" > Sneakers </th> <td class="px-6 py-3">White</td> <td class="px-6 py-3">Shoes</td> <td class="px-6 py-3">23</td> </tr> <tr class="bg-gray-100 dark:bg-zinc-900"> <th scope="row" class="whitespace-nowrap px-6 py-3 font-semibold text-gray-900 dark:text-gray-100" > Dress </th> <td class="px-6 py-3">Red</td> <td class="px-6 py-3">Dresses</td> <td class="px-6 py-3">11</td> </tr> </tbody> </table></div>
---interface TableItem { name: string; color: string; type: string; stock: string;}/* You can use this interface above to define the structure of your data and receive it as a prop */
const tableData: TableItem[] = [ { name: "T-Shirt", color: "Blue", type: "Tops", stock: "15", }, { name: "Jeans", color: "Black", type: "Bottoms", stock: "32", }, { name: "Sneakers", color: "White", type: "Shoes", stock: "23", }, { name: "Dress", color: "Red", type: "Dresses", stock: "11", },];---
{ tableData.length > 0 && ( <div class="relative m-2 overflow-x-auto rounded-lg border border-gray-300 shadow-md dark:border-zinc-800 dark:shadow-lg dark:shadow-black"> <table class="w-full rounded-lg text-left text-base text-gray-800 shadow-md dark:bg-zinc-950 dark:text-gray-300"> <caption class="caption-top bg-white p-4 text-start text-xl font-semibold dark:bg-zinc-950"> Products <p class="mb-2 mt-2 text-base font-normal text-gray-600 dark:text-gray-400"> This is the available stock for the products listed below as of 02/05/2024 </p> </caption> <thead class="bg-gray-200 text-sm uppercase text-gray-800 dark:bg-zinc-800 dark:text-gray-300"> <tr class="border border-gray-300 dark:border-zinc-700"> <th scope="col" class="px-6 py-3"> Product </th> <th scope="col" class="px-6 py-3"> Color </th> <th scope="col" class="px-6 py-3"> Type </th> <th scope="col" class="px-6 py-3"> Stock </th> </tr> </thead> <tbody class="divide-y divide-gray-300 dark:divide-zinc-800"> {tableData.map((item) => ( <tr class="odd:bg-white even:bg-gray-100 odd:dark:bg-zinc-950 even:dark:bg-zinc-900"> <th scope="row" class="whitespace-nowrap px-6 py-3 font-semibold text-gray-900 dark:text-gray-100" > {item.name} </th> <td class="px-6 py-3">{item.color}</td> <td class="px-6 py-3">{item.type}</td> <td class="px-6 py-3">{item.stock}</td> </tr> ))} </tbody> </table> </div> )}
Table With Conditional Styling
A table with conditional styling for cells based on the value of a column.
Product | Color | Type | Stock |
---|---|---|---|
T-Shirt | Blue | Tops | 15 |
Jeans | Black | Bottoms | 32 |
Sneakers | White | Shoes | 23 |
DressLimited stock | Red | Dresses | 11 |
SkirtRunning out | Green | Skirts | 7 |
Shirt | Blue | Tops | 25 |
Jeans | Black | Pants | 18 |
---interface TableItem { name: string; color: string; type: string; stock: number;}/* You can use this interface above to define the structure of your data and receive it as a prop */
const tableData: TableItem[] = [ { name: "T-Shirt", color: "Blue", type: "Tops", stock: 15, }, { name: "Jeans", color: "Black", type: "Bottoms", stock: 32, }, { name: "Sneakers", color: "White", type: "Shoes", stock: 23, }, { name: "Dress", color: "Red", type: "Dresses", stock: 11, }, { name: "Skirt", color: "Green", type: "Skirts", stock: 7, }, { name: "Shirt", color: "Blue", type: "Tops", stock: 25, }, { name: "Jeans", color: "Black", type: "Pants", stock: 18, },];---
{ tableData.length > 0 && ( <div class="relative m-2 overflow-x-auto rounded-lg border border-gray-300 shadow-md dark:border-zinc-800 dark:shadow-lg dark:shadow-black"> <table class="w-full rounded-lg text-left text-base text-gray-800 shadow-md dark:text-gray-300"> <thead class="bg-gray-200 text-sm uppercase text-gray-800 dark:bg-zinc-800 dark:text-gray-300"> <tr> <th scope="col" class="px-6 py-3"> Product </th> <th scope="col" class="px-6 py-3"> Color </th> <th scope="col" class="px-6 py-3"> Type </th> <th scope="col" class="px-6 py-3"> Stock </th> </tr> </thead> <tbody class="divide-y divide-gray-300 dark:divide-zinc-800"> {tableData.map((item) => ( <tr class={`${ item.stock > 14 ? "" : item.stock > 10 ? "bg-yellow-200 dark:bg-yellow-900" : "bg-red-800 text-white dark:bg-red-900" }`} > <th scope="row" class="whitespace-normal px-6 py-3 font-semibold"> {item.name}
{item.stock > 14 ? ( "" ) : item.stock > 10 ? ( <span class="ml-3 text-sm font-normal">Limited stock</span> ) : ( <span class="ml-3 text-sm font-normal">Running out</span> )} </th> <td class="px-6 py-3">{item.color}</td> <td class="px-6 py-3">{item.type}</td> <td class={`${item.stock === 15 ? "bg-cyan-200 px-6 py-3 text-black dark:bg-cyan-800 dark:text-white" : "px-6 py-3"}`} > {item.stock} </td> </tr> ))} </tbody> </table> </div> )}
Accessibility Note: While the colors are a good visual indicator, for accessibility ravixUI has added text in the table header for both visual and screen reader users. This is just one approach, you can also add another column to represent the same. The styling in this table is just a showcase of the different ways you can use conditional styling. In a real-world scenario, you would likely stick to one format for styling.
Accessibility Testing Status
Component | NVDA | Windows Narrator | WAVE | Axe | IBM Equal Access |
---|---|---|---|---|---|
Simple Table | Yes | Yes | Yes | Yes | Yes |
Thematic Table | Yes | Yes | Yes | Yes | Yes |
Table With Cell Borders | Yes | Yes | Yes | Yes | Yes |
Table With A Caption | Yes | Yes | Yes | Yes | Yes |
The tables adhere to the WCAG 2.1 AA standards for accessibility.
Additional manual testing maybe required to ensure the tables are accessible in your specific use case.