CSS Lists & Tables Tutorial
In this comprehensive tutorial, you'll learn how to style HTML lists and tables using CSS. We'll cover everything from basic list styling to advanced table techniques for responsive design.
What You'll Learn:
- List styling properties and values
- Creating custom list markers
- Table border and spacing control
- Zebra striping for better readability
- Responsive table techniques
1. CSS List Style Properties
Understanding List Styling
CSS provides three main properties for styling lists:
list-style-type
Defines the type of list item marker. Common values include:
disc,circle,square(for unordered lists)decimal,lower-roman,upper-romanlower-alpha,upper-alphanone(removes markers)
list-style-position
Controls where the marker is positioned relative to list content:
outside(default) - Marker outside content flowinside- Marker inside content, text wraps around
list-style-image
Uses a custom image as list marker instead of default bullets/numbers:
list-style-image: url('bullet.png');list-style: none; to remove default styling and create custom markers with ::before pseudo-element.Practical Examples
CSS Example
/* Different list style types */
ul.circle-list {
list-style-type: circle;
margin-bottom: 20px;
}
ul.square-list {
list-style-type: square;
margin-bottom: 20px;
}
ol.decimal-list {
list-style-type: decimal;
margin-bottom: 20px;
}
ol.roman-list {
list-style-type: upper-roman;
margin-bottom: 20px;
}
/* List position examples */
.position-inside {
list-style-position: inside;
background-color: #f8f9fa;
padding: 15px;
border-radius: 5px;
}
.position-outside {
list-style-position: outside;
background-color: #e9ecef;
padding: 15px;
border-radius: 5px;
}
/* Shorthand property */
.list-shorthand {
list-style: square inside; /* type and position */
}2. Creating Custom List Markers
Why Custom Markers?
While list-style-image allows image markers, using CSS pseudo-elements (::before) gives you more control over styling, positioning, and animations.
Steps to Create Custom Markers:
- Remove default markers:
list-style: none; - Remove default padding:
padding-left: 0; - Add custom padding for new markers
- Use
::beforepseudo-element to create markers - Position markers absolutely relative to list items
Basic Custom Marker Structure:
.custom-list {
list-style: none;
padding-left: 0;
}
.custom-list li {
position: relative;
padding-left: 30px;
margin-bottom: 8px;
}
.custom-list li::before {
content: "•"; /* or "✓", "→", etc. */
position: absolute;
left: 0;
color: #007bff;
font-size: 20px;
}Advanced Custom Markers
CSS Example
/* Checkmark list */
.checkmark-list {
list-style: none;
padding-left: 0;
}
.checkmark-list li {
position: relative;
padding-left: 35px;
margin-bottom: 12px;
line-height: 1.5;
}
.checkmark-list li::before {
content: "✓";
position: absolute;
left: 0;
top: 0;
width: 24px;
height: 24px;
background-color: #28a745;
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
}
/* Numbered list with custom styling */
.custom-numbered {
list-style: none;
counter-reset: custom-counter;
padding-left: 0;
}
.custom-numbered li {
counter-increment: custom-counter;
position: relative;
padding-left: 50px;
margin-bottom: 15px;
padding-top: 8px;
padding-bottom: 8px;
background-color: #f8f9fa;
border-radius: 5px;
}
.custom-numbered li::before {
content: counter(custom-counter);
position: absolute;
left: 10px;
top: 50%;
transform: translateY(-50%);
width: 30px;
height: 30px;
background-color: #007bff;
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
}
/* Icon list using Unicode/Emoji */
.icon-list {
list-style: none;
padding-left: 0;
}
.icon-list li {
padding-left: 40px;
margin-bottom: 10px;
position: relative;
}
.icon-list li::before {
position: absolute;
left: 0;
font-size: 20px;
}
.icon-list .info::before {
content: "ℹ️";
}
.icon-list .warning::before {
content: "⚠️";
}
.icon-list .success::before {
content: "✅";
}3. Table Borders & Spacing Control
Border Models in CSS Tables
CSS offers two border models for tables, controlled by the border-collapse property:
border-collapse: collapse;
- Adjacent borders merge into a single border
- More traditional table appearance
- No space between cells
border-spacingproperty is ignored
border-collapse: separate;
- Each cell maintains its own borders
- Space between cells controlled by
border-spacing border-spacing: horizontal vertical;- Allows for rounded corners on individual cells
collapse for data-heavy tables and separatewhen you need visual separation between cells or rounded corners.Key Properties:
border-collapse: Sets table border modelborder-spacing: Space between cells (separate model only)empty-cells: Show/hide borders of empty cellstable-layout:auto(default) vsfixed
Border Examples Comparison
CSS Example
/* Collapse border model */
.collapse-example {
border-collapse: collapse;
width: 100%;
margin-bottom: 30px;
}
.collapse-example th,
.collapse-example td {
border: 1px solid #dee2e6;
padding: 12px;
text-align: left;
}
.collapse-example th {
background-color: #007bff;
color: white;
font-weight: 600;
}
.collapse-example tr:nth-child(even) {
background-color: #f8f9fa;
}
/* Separate border model */
.separate-example {
border-collapse: separate;
border-spacing: 10px 5px;
width: 100%;
margin-bottom: 30px;
}
.separate-example th,
.separate-example td {
border: 2px solid #6c757d;
padding: 12px;
text-align: center;
background-color: white;
}
.separate-example th {
background-color: #6c757d;
color: white;
font-weight: 600;
}
/* Fixed table layout */
.fixed-layout {
table-layout: fixed;
width: 100%;
border-collapse: collapse;
}
.fixed-layout th,
.fixed-layout td {
border: 1px solid #ddd;
padding: 10px;
text-align: left;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.fixed-layout th:nth-child(1),
.fixed-layout td:nth-child(1) { width: 40%; }
.fixed-layout th:nth-child(2),
.fixed-layout td:nth-child(2) { width: 30%; }
.fixed-layout th:nth-child(3),
.fixed-layout td:nth-child(3) { width: 30%; }4. Zebra Striping for Better Readability
The :nth-child() Pseudo-class
Zebra striping (alternating row colors) improves table readability. CSS provides the powerful:nth-child() pseudo-class for this purpose.
:nth-child() Patterns:
tr:nth-child(even)Selects even rows (2nd, 4th, 6th...)tr:nth-child(odd)Selects odd rows (1st, 3rd, 5th...)tr:nth-child(3n)Every 3rd row (3rd, 6th, 9th...)tr:nth-child(3n+1)Every 3rd starting from 1st (1st, 4th, 7th...)tr:nth-child(2n+1)Same as odd (1st, 3rd, 5th...)Benefits of Zebra Striping:
- Improves readability of large data sets
- Helps users follow rows across wide tables
- Reduces eye strain during prolonged reading
- Adds visual appeal without being distracting
Advanced Zebra Striping Techniques
CSS Example
/* Basic zebra striping */
.zebra-basic {
width: 100%;
border-collapse: collapse;
margin-bottom: 30px;
}
.zebra-basic th {
background-color: #2c3e50;
color: white;
padding: 15px;
text-align: left;
font-weight: 600;
border-bottom: 3px solid #1a252f;
}
.zebra-basic td {
padding: 12px 15px;
border-bottom: 1px solid #e0e0e0;
}
.zebra-basic tr:nth-child(even) {
background-color: #f8f9fa;
}
.zebra-basic tr:nth-child(odd) {
background-color: white;
}
.zebra-basic tr:hover {
background-color: #e3f2fd;
transition: background-color 0.2s ease;
}
/* Triple striping pattern */
.triple-stripe {
width: 100%;
border-collapse: collapse;
margin-bottom: 30px;
}
.triple-stripe th {
background-color: #6c757d;
color: white;
padding: 12px;
text-align: left;
}
.triple-stripe td {
padding: 10px 12px;
border-bottom: 1px solid #dee2e6;
}
.triple-stripe tr:nth-child(3n+1) {
background-color: white;
}
.triple-stripe tr:nth-child(3n+2) {
background-color: #f8f9fa;
}
.triple-stripe tr:nth-child(3n+3) {
background-color: #e9ecef;
}
/* Column striping */
.column-stripe {
width: 100%;
border-collapse: collapse;
}
.column-stripe th,
.column-stripe td {
padding: 12px;
text-align: left;
border: 1px solid #dee2e6;
}
.column-stripe th {
background-color: #495057;
color: white;
}
.column-stripe td:nth-child(even),
.column-stripe th:nth-child(even) {
background-color: #f8f9fa;
}
.column-stripe td:nth-child(odd),
.column-stripe th:nth-child(odd) {
background-color: white;
}
.column-stripe tr:hover td {
background-color: #e3f2fd !important;
}5. Responsive Table Techniques
The Challenge of Tables on Mobile
Traditional HTML tables are inherently horizontal and don't work well on small screens. We need CSS techniques to make tables responsive.
Common Responsive Approaches:
1. Horizontal Scrolling
Wrap table in a container with overflow-x: auto
- Pros: Preserves all data, simple to implement
- Cons: Requires horizontal scrolling
2. Stacked/Card Layout
Convert rows to cards on small screens
- Pros: Mobile-friendly, no horizontal scroll
- Cons: Complex CSS, hides column relationships
3. Column Toggle
Hide less important columns on mobile
- Pros: Keeps table structure
- Cons: Hides some data
4. Data Attribute Approach
Show column headers via data-* attributes
- Pros: Clear labels, good UX
- Cons: Requires HTML modification
display: none with media queries.Responsive Table Implementations
CSS Example
/* Method 1: Horizontal Scroll */
.scroll-container {
overflow-x: auto;
-webkit-overflow-scrolling: touch; /* Smooth scroll on iOS */
margin-bottom: 30px;
border: 1px solid #dee2e6;
border-radius: 5px;
}
.scroll-table {
min-width: 800px; /* Force horizontal scroll */
border-collapse: collapse;
width: 100%;
}
.scroll-table th,
.scroll-table td {
padding: 12px;
border: 1px solid #dee2e6;
text-align: left;
white-space: nowrap; /* Prevent text wrapping */
}
.scroll-table th {
background-color: #495057;
color: white;
position: sticky;
top: 0;
}
/* Method 2: Card Layout for Mobile */
.card-table-container {
width: 100%;
}
.card-table {
width: 100%;
border-collapse: collapse;
}
/* Default desktop styles */
.card-table th,
.card-table td {
padding: 12px;
border: 1px solid #dee2e6;
text-align: left;
}
.card-table th {
background-color: #343a40;
color: white;
}
/* Mobile styles */
@media (max-width: 768px) {
.card-table,
.card-table thead,
.card-table tbody,
.card-table th,
.card-table td,
.card-table tr {
display: block;
}
.card-table thead tr {
position: absolute;
top: -9999px;
left: -9999px;
}
.card-table tr {
margin-bottom: 15px;
border: 1px solid #dee2e6;
border-radius: 5px;
padding: 10px;
background-color: white;
}
.card-table td {
display: block;
text-align: right;
position: relative;
padding-left: 50%;
border: none;
border-bottom: 1px solid #eee;
}
.card-table td:last-child {
border-bottom: 0;
}
.card-table td::before {
content: attr(data-label);
position: absolute;
left: 10px;
width: 45%;
padding-right: 10px;
text-align: left;
font-weight: bold;
color: #495057;
}
}
/* Method 3: Hide columns on mobile */
.responsive-hide {
width: 100%;
border-collapse: collapse;
}
.responsive-hide th,
.responsive-hide td {
padding: 10px;
border: 1px solid #dee2e6;
}
.responsive-hide th {
background-color: #6c757d;
color: white;
}
/* Hide less important columns on mobile */
@media (max-width: 768px) {
.mobile-hide {
display: none;
}
}Summary & Best Practices
Key Takeaways:
- Use
list-style: nonewith::beforepseudo-elements for custom list markers - Choose
border-collapse: collapsefor data tables andseparatefor styled tables - Implement zebra striping with
:nth-child(even/odd)for better readability - Always make tables responsive - horizontal scroll is the simplest approach
- Consider card layout for complex tables on mobile devices
Accessibility Tips:
- Ensure sufficient color contrast for striped tables
- Use
<th scope="col">and<th scope="row">for table headers - Add
<caption>element to describe table content - For complex tables, use
aria-describedbyto link to descriptions
Next Steps:
Practice these techniques with your own data. Experiment with different color schemes, try implementing a responsive table for a real project, and test accessibility with screen readers.