Editor’s note: This article was last updated by David Omotayo on 24 May 2024 to demonstrate how to add interactive elements to give your data tables the appearance of a CSS sort and filter functionality.
Data tables are an important part of many websites and applications as they allow us to display complex data in a way that’s easy to reference. We can also use data tables to organize and analyze or compare data from different sources.
However, many data tables are designed for desktop use and are difficult to view and interact with on mobile devices. On smaller screens, the cell content wraps, making it difficult to read and forcing the user to scroll or zoom in to see the data.
In this article, we’ll explore options for creating responsive data tables using CSS and techniques for making them accessible and easy to use on mobile devices.
A responsive data table is designed to work well on both desktop and mobile devices. When a user views a responsive data table on a mobile device, the data table will automatically adjust to fit the screen size, making it easy to view and interact with the data. And when the user views the same data table on a desktop device, the data table will automatically adjust to fit the screen size, making it easy to view and interact with.
When creating a responsive data table, the first step is to properly define its structure. Having the correct structure will make the table easier to style and maintain and will also help ensure it is accessible.
A typical HTML data table consists of the following elements:
<table>
: Used to define the data table; it contains the <thead>
, <tbody>
, and <tfoot>
elements<thead>
: Used to define the header of the data table; it contains the <tr>
element, which contains the <th>
elements<tbody>
: Used to define the body of the data table; it contains the <tr>
elements, which contain the <td>
elements<tfoot>
: Used to define the footer of the data table; it contains the <tr>
element, which contains the <td>
elementsIn practice, a basic HTML data table will look like this:
<table> <thead> <tr> <th>Column 1</th> <th>Column 2</th> <th>Column 3</th> </tr> </thead> <tbody> <tr> <td>Row 1, Column 1</td> <td>Row 1, Column 2</td> <td>Row 1, Column 3</td> </tr> <tr> <td>Row 2, Column 1</td> <td>Row 2, Column 2</td> <td>Row 2, Column 3</td> </tr> <tr> <td>Row 3, Column 1</td> <td>Row 3, Column 2</td> <td>Row 3, Column 3</td> </tr> </tbody> <tfoot> <tr> <td>Footer 1</td> <td>Footer 2</td> <td>Footer 3</td> </tr> </tfoot> </table>
By organizing the data table into the appropriate HTML elements, we can lay a solid foundation for applying CSS styles and implementing responsive design techniques. This structured approach makes it easier to manage the data table’s content, enhance its functionality, and optimize its display across different devices and screen sizes.
Without any styling, our HTML example table will look like this:
The browser will automatically add some default styling to the table, but not enough to make the table look good! Fortunately, we can improve the table’s appearance with some basic CSS:
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; } body { padding: 20px; } table { border-collapse: collapse; } th, td { padding: 8px; text-align: left; border: 1px solid #ddd; } tbody tr:hover { background-color: #f5f5f5; } th { background-color: #4caf50; color: white; } tfoot { background-color: #4a524a; color: white; }
Even on the smallest of screens, this basic table with simple styling will display the data in a way that is easy to read and understand. But when we start to add more data and more rows, it begins to look cluttered and becomes difficult to read:
This is where the need for responsive design comes in.
When designing a responsive data table, it’s important to consider the different screen sizes and devices that the table will be viewed on. This will help us determine the best way to display the data.
Here’s an example of a table containing employee data. The table is not responsive, and the data in the cells is not aligned properly. When the screen size becomes smaller than the table’s width, the data in the cells will start to wrap, making it difficult to read:
To make the table responsive, we need to write some CSS. Let’s look at some responsive data table techniques.
Controlling the table’s width is one of the most important things we can do to make it responsive. This ensures that the table doesn’t distort the layout of the page.
By wrapping the table in a <div>
element and setting the width of the <div>
element to 100%
or the maximum width required, we can make the table scrollable when the screen size or table wrapper becomes smaller than the table’s width, thus preventing the table from distorting the layout of the page:
<style> .table-wrapper { width: 100%; /* max-width: 500px; */ overflow-x: auto; } </style> <body> <div class="table-wrapper"> <table> ... </table> </div> </body>
By default, when a table is displayed in the browser, it tries to fit the table within the screen size or the width of its container. This can result in columns being resized and the content of cells wrapping into multiple lines. The browser breaks the content at word spaces, and if there is no more space, the table will start scrolling horizontally.
This wrapping of content can lead to rows having varying heights, making the table appear messy and challenging to read. Additionally, because column widths depend on the width of their largest cells, the columns may have different widths, further contributing to the untidy appearance of the table and making it difficult to read.
There are multiple approaches to address this wrapping issue depending on the desired data display. Let’s look at a few.
min-width
and max-width
We can adjust the column widths based on the maximum content length, using CSS properties like min-width
and max-width
, which prevent content wrapping and ensure that all columns have consistent widths. This approach is useful when you don’t want the content to wrap, but it is not always practical because the maximum content length may be unknown or hard to determine.
Also, it can be challenging to set column widths manually. Another tricky scenario is when one column needs to accommodate a single cell with extensive content while others have minimal content. This can result in excessively wide columns and an untidy appearance:
td:first-child { min-width: 850px; max-width: 850px; }
Here we’ve used the CSS min-width
and max-width
properties to set the first column to have a minimum and maximum width of 850px
. This ensures that the column will always be 850px
wide, regardless of the content. This approach works best for scenarios in which you have fairly consistent data lengths.
width
Another approach is to assign fixed widths to the columns while allowing the content to wrap within them. With this approach, columns will have consistent widths, but the table may still have an untidy appearance as the wrapped content can result in rows of different heights:
table { width: 800px; } th, td { width: calc(800px / 3); }
Here we’ve set the table to have a width of 800px
and the columns to have a width of 800px
divided by the number of columns, which is 3
in this case. This ensures that the columns will always be the same width, regardless of the content. This approach works well for scenarios where visual integrity is a priority and it’s acceptable to vary row heights.
A third approach is to truncate or slice the content of the cells to ensure consistent row and column widths and heights. By limiting the content’s display length, we can maintain uniformity within the table.
This technique is especially useful when dealing with long text or large amounts of data. However, it’s important to consider the context and purpose of the table.
In some cases, truncating or slicing cell content may compromise readability or result in the loss of important information. It’s crucial to evaluate the specific requirements and priorities of the data being presented before deciding whether to slice the content.
Here’s an example of a data table with truncated cells:
th, td { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } td:first-child { min-width: 200px; max-width: 200px; }
Here we’ve set the first column to have a minimum and maximum width of 200px
. This ensures that the column will always be 200px
wide, regardless of the content. We’ve also set the white-space
property to nowrap
to prevent the content from wrapping and the overflow
property to hidden
to hide any content that overflows the cell. Finally, we’ve set the text-overflow
property to ellipsis
to add an ellipsis to the end of the content that overflows the cell.
This approach works best for scenarios where it’s critical to maintain consistent row and column dimensions.
word-break
On some occasions, we may encounter tables containing long links or words that can cause the table to overflow and disrupt the layout. This can make the table difficult to read and interpret. To address this issue, we can use the word-break
CSS property to break long words and links and prevent them from disrupting the table’s layout.
Here’s an example:
<div class="table-wrapper"> <table> <thead> <tr> <th>Column 1</th> <th>Column 2 with long links/words</th> </tr> </thead> <tbody> <tr> <td>Row 1, Column 1</td> <td>https://averylonglink.com/scdnddsffrffjfsjfdfdfdcndfdjfkfdf33rcfrerrfdfcrerecfewreceewrwercrerecrererereerererergrtcghvtvhtrvfgf</td> </tr> <tr> <td>Row 2, Column 1</td> <td>Honorificabilitudinitatibus califragilisticexpialidocious Taumatawhakatangihangakoauauotamateaturipukakapikimaungahoronukupokaiwhenuakitanatahu</td> </tr> <!-- Add more rows as needed --> </tbody> </table> </div> .table-wrapper { width: 100%; max-width: 500px; overflow-x: auto; } th, td { padding: 8px; text-align: left; border: 1px solid #ddd; } td:first-child { min-width: 150px; max-width: 150px; } td:last-child { word-break: break-all; }
In the code above, if we comment out the word-break
and overflow
CSS properties to prevent them from being applied to the table, the contents of the second column stretch the width of the table past the intended maximum width of 500px
:
By applying the word-break
and overflow
CSS properties to the table, we see that the table now retains our desired width
. And at the same time, the long content is broken into multiple lines, making it easier to read:
For additional tools to implement responsive design, including image galleries and web layouts, explore our guides on using CSS flexbox and CSS grid for these techniques.
Media queries enable us to apply conditions to how CSS properties appear, based on the screen size or device that the page is being viewed on. This allows us to create a responsive design that will look good on all devices.
Here are some of the CSS properties that we can adjust with media queries:
text-align
, can be modified to ensure that the content within the table is properly aligned based on the screen size. Additionally, text formatting properties, such as text-overflow
and white-space
, may be adjusted to control how text wraps or truncates within table cells on smaller screensThese are just a few examples of the CSS properties that can be adjusted with media queries to create responsive tables. The specific properties you choose to modify will depend on your table’s design, content, and the desired user experience on different devices.
Accessibility is an essential aspect of web design. It ensures that people with disabilities can access and use websites and web applications. It helps improve the user experience for everyone, including those without disabilities.
Data tables that display large amounts of information can be difficult for people with disabilities to navigate and find the information they need. It may be helpful to test your data table with a screen reader, such as AWS, NVDA, and VoiceOver. Using this type of assistive technology to the table’s content and structure will help you identify any potential issues and ensure that the table is properly interpreted and navigable by individuals with visual disabilities.
It’s also a good idea to test your data table with keyboard navigation. Individuals with motor disabilities may have difficulty using a mouse or other pointing device and may need to rely on keyboard navigation to move through a data table.
There are several ways to make data tables accessible; let’s take a look.
Semantic HTML is a way of writing HTML that makes it easier for people who use assistive technologies to understand the content. It also makes it easier for search engines to understand the content.
Here are some semantic HTML elements that can be used to make data tables accessible:
<table>
: Used to define a table; it can be used to make data tables accessible by providing a structure for the table<caption>
: Used to provide a caption for a table; it can be used to make data tables accessible by describing the table’s purpose<thead>
: Used to define the table’s header; it can make data tables accessible by providing a structure for the table’s header<tbody>
: Used to define the table’s body; it can make data tables accessible by providing a structure for the table’s body<tfoot>
: Used to define the table’s footer; it can make data tables accessible by providing a structure for the table’s footer<tr>:
Used to define a row in a table; it can make data tables accessible by providing a structure for the table’s rows<th>
: Used to define a header cell in a table; it can make data tables accessible by providing a structure for the table’s header cells<td>
: Used to define a data cell in a table; it can make data tables accessible by providing a structure for the table’s data cellsHere’s an example of a data table with semantic HTML:
<table> <caption>Employee Information</caption> <thead> <tr> <th scope="col">ID</th> <th scope="col">Name</th> <th scope="col">Position</th> </tr> </thead> <tbody> <tr> <td>001</td> <td>John Smith</td> <td>Manager</td> </tr> <tr> <td>002</td> <td>Jane Doe</td> <td>Developer</td> </tr> </tbody> <tfoot> <tr> <td colspan="3">Total employees: 2</td> </tr> </tfoot> </table>
scope
attributeThe scope
attribute is used to define the scope of the data table’s header cell, <th>
. It helps assistive technologies, such as screen readers, understand the relationships between header cells and data cells in the table.
The scope
attribute can have the following values:
row
: Indicates that the header cell applies to a row of data cells. It associates the header cell with the data cells in the same rowcol
: Indicates that the header cell applies to a column of data cells. It associates the header cell with the data cells in the same columnrowgroup
: Indicates that the header cell applies to a group of rowscolgroup
: Indicates that the header cell applies to a group of columnsBy using the scope
attribute appropriately, screen readers can announce the headers when reading the table, providing context and improving the understanding of the table’s structure. Here’s an example:
<table> <caption>Monthly Sales Report</caption> <thead> <tr> <th scope="col">Month</th> <th scope="col">Product A</th> <th scope="col">Product B</th> <th scope="col">Product C</th> </tr> </thead> <tbody> <tr> <th scope="row">January</th> <td>$1000</td> <td>$1500</td> <td>$1200</td> </tr> <tr> <th scope="row">February</th> <td>$900</td> <td>$1800</td> <td>$1100</td> </tr> <tr> <th scope="row">March</th> <td>$1200</td> <td>$1300</td> <td>$1000</td> </tr> </tbody> </table>
The above table represents a monthly sales report with three products (A
, B
, and C
) and their corresponding sales figures for each month. The scope
attribute is used in the <th>
elements to define the scope of the header cells.
The scope="col"
attribute is used for the header cells in the table header row <head>
, indicating that each header cell applies to a column.
The scope="row"
attribute is used for the header cells in the table body rows, <tbody>
, indicating that each header cell applies to a row.
By using the scope
attribute appropriately, assistive technologies can associate the header cells with their corresponding data cells, improving the accessibility and understanding of the table’s structure for users with disabilities.
summary
attributeThe summary
attribute is used to provide a summary of the table’s purpose and structure. It also helps assistive technologies, such as screen readers, understand the table’s purpose and structure.
Here’s an example of how the summary attribute can be used:
<table summary="Monthly sales report for products A, B, and C"> <!-- table content goes here --> </table>
In the above code, the summary attribute is added to the <table>
element, and its value is set to "Monthly sales report for products A, B, and C"
.
When assistive technologies encounter a table with a summary attribute, they can read out the summary to provide users with an understanding of what the table contains and its purpose. This is especially valuable for users with visual impairments who rely on screen readers to navigate web content.
Including a meaningful and informative summary attribute is an important practice to enhance the accessibility of data tables, as it allows users to quickly grasp the table’s purpose and context without having to analyze the entire content.
ARIA (Accessible Rich Internet Applications) attributes are also used to enhance the accessibility of web content. They provide additional information about the purpose and structure of web content, allowing assistive technologies to better understand and navigate the content.
One commonly used ARIA attribute is the role
attribute. This attribute specifies the semantic role
of an element, helping assistive technologies understand its purpose. For data tables, the role
attribute is typically set to "table"
to indicate that the element represents a table.
Here’s an example of how the role
attribute can be used with a table:
<table role="table"> <thead role="rowgroup"> <tr role="row"> <th role="columnheader">Name</th> <th role="columnheader">Age</th> </tr> </thead> <tbody role="rowgroup"> <tr role="row"> <td role="cell">John</td> <td role="cell">25</td> </tr> <tr role="row"> <td role="cell">Jane</td> <td role="cell">30</td> </tr> </tbody> </table>
Here is another example of how the role
attribute can be used with a table that doesn’t use the semantic HTML elements:
<div role="table"> <div role="rowgroup"> <div role="row"> <div role="columnheader">Name</div> <div role="columnheader">Age</div> </div> <div role="row"> <div role="cell">John</div> <div role="cell">25</div> </div> <div role="row"> <div role="cell">Jane</div> <div role="cell">30</div> </div> </div> </div>
Several other ARIA attributes can be used to enhance the accessibility of data tables, such as the following:
aria-label
: Used to provide a label for an element. It can be used to provide a label for a table, row, column, or cellaria-sort
: Used to indicate the default sorting order of a sortable column in the table. It can have values like “ascending”, “descending”, or “none”aria-rowindex
: Used to indicate the row index of a row in the tablearia-labelledby
: Used to provide a label for an element using the ID of another element. It can be used to provide a label for a table, row, column, or cellaria-describedby
: Used to describe an element using the id of another element. It can be used to describe a table, row, column, or cellThere are several other ARIA attributes that can be used to enhance the accessibility of data tables. For more information, refer to the W3C ARIA specification.
Color contrast is another important accessibility feature that ensures that text is readable against its background. It is especially useful for individuals with visual impairments who may have difficulty reading text that is not sufficiently contrasted.
You can use a color contrast checker to test the accessibility of data tables. There are several color contrast checkers available, including WebAIM’s color contrast checker.
Alternative text (or alt text) is an important accessibility feature that provides a text alternative for images. It is especially useful for individuals with visual impairments who may have difficulty viewing images.
By adding alternative text to images in data tables, we can help ensure that the table is properly interpreted and navigable by individuals using assistive technologies.
Adding sorting and filtering functionalities to a data table typically requires JavaScript because CSS alone is not designed to handle data manipulation or interaction. CSS is primarily used for styling the layout and can’t modify the HTML structure dynamically.
However, with some clever workarounds, you can give the appearance of a CSS table sort and filter by adding interactive arrow icons that change orientation based on the sorted state next to the table headers. However, the actual implementation would still need JavaScript.
The idea is to use the CSS ::after
psuedo-element on the table header (th
) elements to add the Unicode character for arrow ("\25B2"
) to the content
property to display an up arrow:
th::after { content: '\25B2'; position: absolute; right: 5px; font-size: 10px; }
Then, you can add interactivity using the ::active
pseudo-class:
th:active::after { transform: rotate(180deg); }
This is a basic example. You can customize the styles and icons to match your design preferences. However, keep in mind that this approach only helps with the visual cue for sorting; it doesn’t actually sort the data.
In this article, we discussed several CSS strategies for optimizing data tables on smaller screens. Each approach has distinct use cases, and it’s essential to consider your specific requirements before choosing one.
It’s also possible to combine multiple approaches within a single data table by applying different approaches to specific columns or rows. This allows for a more tailored approach to handle specific content and achieve the desired display outcome.
For example, you might choose to set fixed widths for columns with shorter content, truncate or slice long text in certain columns, and use flexible widths for others. This flexibility allows for a customized solution that balances readability and visual consistency. Consider your data context, user experience goals, and tradeoffs involved to make an informed decision.
For more tips on improving reponsive data tables using CSS, check out “Improving responsive data table UX with CSS.” You can also view and play with all the code snippets from this guide on CodePen.
As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app, mobile app, or website. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.
Modernize how you debug web and mobile apps — start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowIn this article, you’ll learn how to set up Hoppscotch and which APIs to test it with. Then we’ll discuss alternatives: OpenAPI DevTools and Postman.
Learn to migrate from react-native-camera to VisionCamera, manage permissions, optimize performance, and implement advanced features.
SOLID principles help us keep code flexible. In this article, we’ll examine all of those principles and their implementation using JavaScript.
JavaScript’s Date API has many limitations. Explore alternative libraries like Moment.js, date-fns, and the new Temporal API.