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-roman
  • lower-alpha, upper-alpha
  • none (removes markers)
list-style-position

Controls where the marker is positioned relative to list content:

  • outside (default) - Marker outside content flow
  • inside - 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');
💡 Pro Tip: Use 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:
  1. Remove default markers: list-style: none;
  2. Remove default padding: padding-left: 0;
  3. Add custom padding for new markers
  4. Use ::before pseudo-element to create markers
  5. 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-spacing property 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
📊 Best Practice: Use collapse for data-heavy tables and separatewhen you need visual separation between cells or rounded corners.
Key Properties:
  • border-collapse: Sets table border model
  • border-spacing: Space between cells (separate model only)
  • empty-cells: Show/hide borders of empty cells
  • table-layout: auto (default) vs fixed

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...)
🎨 Design Tip: Use subtle color differences for zebra striping (e.g., #f8f9fa and white). High contrast can be distracting. Always maintain sufficient contrast for accessibility.
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
📱 Mobile-First Tip: Design tables mobile-first. Consider what data is essential on small screens and hide secondary columns. Use 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: none with ::before pseudo-elements for custom list markers
  • Choose border-collapse: collapse for data tables and separate for 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-describedby to 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.