On this page
Styling Lists & Tables
Styling Lists and Tables in CSS
Lists and tables organise information. Lists group related items such as menus, steps and features. Tables present data in rows and columns. Learning styling lists and tables in CSS helps you build navigation bars, breadcrumbs, step-by-step guides, pricing tables, dashboards and reports that are clean, readable and accessible. In this lesson you will cover list markers, custom counters, horizontal lists, table borders, zebra stripes, hover rows, sticky headers and responsive tables, with live editable code.
Focus keyword: styling lists and tables in CSS. Related keywords: list-style-type, ::marker, CSS counters, border-collapse, border-spacing, caption-side, zebra striping, nth-child, sticky table header, responsive table CSS, tabular-nums, table-layout fixed.
Part 1: Styling Lists
1. List Types in HTML
<ul>: unordered list, shown with bullets by default.<ol>: ordered list, shown with numbers by default.<dl>with<dt>and<dd>: description list for term and definition pairs.
2. The list-style Properties
| Property | What it does | Example values |
|---|---|---|
list-style-type | Sets the marker shape or numbering system | disc, circle, square, decimal, lower-alpha, upper-roman, none |
list-style-position | Puts the marker outside or inside the content box | outside, inside |
list-style-image | Uses an image as the marker | url('icon.svg') |
list-style | Shorthand for the three above | square inside, none |
To remove bullets completely, use list-style: none; padding: 0; margin: 0;. Note that some screen readers (notably VoiceOver with Safari) stop announcing an element as a list when list-style is none, so add role='list' to the element if the list semantics matter.
3. Styling Markers with ::marker
The ::marker pseudo-element targets the bullet or number of a list item. You can change its color, font-size, font-weight, and even its content. Only a limited set of properties is supported, so for heavy customisation use counters or ::before.
4. CSS Counters for Custom Numbering
CSS counters let you build numbered steps, nested outlines (1.1, 1.2) and styled number badges. The three building blocks are counter-reset (create the counter), counter-increment (increase it on each item) and counter() (display the value inside content).
5. Horizontal Lists and Navigation
Most navigation menus are lists. Turn a vertical list into a horizontal one with display: flex and gap. Breadcrumbs can add separators using the adjacent sibling selector: li + li::before { content: '/'; }. Always wrap navigation lists in a <nav> with an aria-label.
Part 2: Styling Tables
1. Semantic Table Structure
A well-built table uses <caption>, <thead>, <tbody>, <tfoot>, <th scope='col'> and <th scope='row'>. These elements give screen readers the context to announce each cell together with its headers. Use tables only for tabular data, never for page layout.
2. Table Properties Reference
| Property | Purpose |
|---|---|
border-collapse | collapse merges neighbouring borders into one, separate keeps them apart |
border-spacing | Gap between cells (only when separate) |
caption-side | Places the caption at the top or bottom |
table-layout | auto sizes by content, fixed uses equal, predictable column widths |
empty-cells | Show or hide borders of empty cells |
vertical-align | Aligns content inside cells (top, middle, bottom) |
font-variant-numeric: tabular-nums | Makes digits equal width so numbers line up in columns |
3. Zebra Striping and Row Hover
Alternating row colours help the eye follow a row across wide tables. Use tbody tr:nth-child(even) for stripes and tbody tr:hover for a highlight. Keep the contrast subtle, and make sure text contrast stays above 4.5:1 on every row colour.
4. Aligning Numbers
Right-align numeric columns and apply font-variant-numeric: tabular-nums so decimals line up. Left-align text columns. Consistent alignment makes comparisons much faster.
5. Sticky Headers
For long tables, keep the header visible while scrolling. Wrap the table in a container with overflow: auto and a fixed max-height, then set position: sticky; top: 0; on the <th> cells in the header. Give the sticky cells a solid background so rows do not show through.
6. Responsive Tables
Tables are naturally wide, so on small screens you have two common strategies:
- Horizontal scroll: wrap the table in a div with
overflow-x: auto. This is the simplest and keeps the structure intact. - Stacked cards: at a small breakpoint, turn each row into a block and show the column name using
content: attr(data-label)ontd::before.
Accessibility Checklist
- Add a
<caption>that describes the table. - Use
<th>withscopefor headers, not styled<td>cells. - Do not rely on colour alone to convey meaning inside cells.
- Keep the scroll container keyboard focusable with
tabindex='0'when it scrolls. - Keep lists as real lists so assistive technology announces the item count.
Trending Techniques (2026)
- ::marker with custom content for branded bullets without extra markup.
- CSS counters and counter-style for multi-level numbering and step wizards.
- :has() to highlight a row or list item based on its content, for example
tr:has(input:checked). - position: sticky headers and first columns for data-heavy dashboards.
- Container queries for tables inside resizable cards.
- tabular-nums for finance and analytics UIs.
Common Mistakes
- Forgetting
border-collapse: collapseand getting double borders. - Using tables for layout.
- Removing list bullets and losing list semantics without adding a role.
- Placing sticky headers without a background colour.
- Leaving wide tables unscrollable on mobile.
Summary
Lists and tables are essential building blocks. With list-style, ::marker and counters you can style any list. With border-collapse, stripes, sticky headers and responsive patterns you can make data tables that are beautiful and usable on every device. Experiment with each live example below.
Press Run to execute.
Press Run to execute.
Press Run to execute.
Press Run to execute.
Press Run to execute.
Press Run to execute.
Press Run to execute.
Press Run to execute.
Turn the ordered list into a styled steps list using CSS counters. Remove the default numbers, then show each step number inside a blue circle using li::before with content: counter(step). Add a light gray background and rounded corners to each item.
Press Run to execute.
Show expected output
Each list item shows a blue circular number badge on the left, a light gray background and rounded corners, with no default browser numbers.This is a self-check — compare your result with the expected output above.
Style the table so borders are collapsed, the header row has a dark background with white text, even body rows have a light gray background, and a row turns light blue on hover. Right-align the Price column and use tabular-nums.
Press Run to execute.
Show expected output
A table with single borders, a dark header, alternating light gray rows, a light blue hover row, and right-aligned aligned numbers in the Price column.This is a self-check — compare your result with the expected output above.
Was this page helpful?