You want your entire table row to be a real clickable link? Here, I present you with a practical and accesible solution.
Link to the codesandbox: Check it out!
Step 1: Create your table
Use whatever technologies you like for this: I went with React and tanstack-table, because that’s what I currently use at work.
type Items = {
ranking: number;
url: string;
name: string;
};
const data: Items[] = [
{
ranking: 1,
url: "https://google.com",
name: "Google",
},
{
ranking: 2,
url: "https://bing.com",
name: "Bing",
},
{
ranking: 3,
url: "https://yahoo.com",
name: "Yahoo",
},
];
const columnHelper = createColumnHelper<Items>();
const columns = [
columnHelper.accessor("ranking", {
cell: (info) => info.getValue(),
}),
columnHelper.accessor("name", {
cell: (info) => info.getValue(),
}),
columnHelper.accessor("url", {
cell: (info) => info.getValue(),
}),
];
export function Table() {
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
});
return (
<div>
<table>
<thead>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th key={header.id} className="table-head">
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => (
<tr key={row.id} className="data-rows">
{row.getVisibleCells().map((cell) => (
<td key={cell.id} className="table-cell">
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
);
}
Step 2: Add an additional column to your table
I added it on the left side, but that’s indifferent. Here, it’s filled with yellow, so you can see it.
Step 3: Put your link inside the empty column
In the small column, put the link that every row should lead to inside an anchor tag.
...
<thead>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{/*⬇️ add an extra column here */}
<th key="extra-column" aria-label="Search Engine Links"></th>
{headerGroup.headers.map((header) => (
<th key={header.id} className="table-head">
...
<tbody>
{table.getRowModel().rows.map((row) => (
<tr key={row.id} className="data-rows">
{/*⬇️ add a cell with an empty link for every row
below the empty column header */}
<td>
<a
href={row.original.url}
aria-label={row.original.url}
className="row-link"
></a>
</td>
{row.getVisibleCells().map((cell) => (
...
Step 4: Position the link so it spans over the whole row
Now, that the link is part of the table, let’s make it overlap over the whole row with CSS:
tr {
border: 1px solid white;
position: relative;
height: 3rem;
}
.row-link {
position: absolute;
top: 0;
left: 0;
content: "";
width: 100%;
height: 3rem;
}
.data-rows:hover {
background-color: #a8a5a5;
}
I added a yellow background and borders to every element, so it’s easier to see. With the absolute positioning in relation to the table row, the anchor tag spans over the whole row and makes it clickable.
By adding an adequate outline to the anchor tag, the focus is also including the whole row and making it therefore great to tab through. 🥳
⚠️Notes about Accessibility⚠️
- 🧑🏽🦯 The empty column header and the empty anchor tags can be confusing for people using screen readers, this is why I added an aria-attribute to them.
- ⤵️ I customized the outline for focus-visible, so the links are clearly marked as something clickable and accessible over the keyboard.
- 🎨 Mine is of course an example to show the concept - always check for sufficient contrast when choosing your colors.
I love this solution - it's so elegant! Hope this helped you!
Top comments (2)
Nice, thanks for a nice and simple way. I guess, another way could have been to add the link column as the first column in columns list and let
cell
field return the link tag? What do you think about that? I am new to tanstack table and wondering when it's good to define all columns in the columns array vs adding columns in therender
JSX.position: relative isn't supposed to work on tr elements, and e.g. does not work in Safari.