List Styling

What You'll Learn

  • How to use list-style-type to control marker appearance (disc, circle, square, decimal, alpha, roman)
  • Master list-style-position for inside and outside marker placement
  • Create custom markers using the modern ::marker pseudo-element
  • Design custom bullets with the ::before pseudo-element for maximum control
  • Use CSS counters with counter-reset, counter-increment, and counter()
  • Style nested lists with different markers at each level
  • Build modern list designs including card-style, checklists, and feature lists
  • Understand browser support for list styling features

Introduction to List Styling

Lists are fundamental HTML elements used to organize related items, steps, or information. CSS provides powerful tools to transform basic bulleted and numbered lists into visually compelling components that enhance readability and user experience. From simple marker customization to complex counter systems, list styling gives you complete control over how sequential information appears on your page.

The default browser styling for lists is functional but limited. By mastering list styling techniques, you can create everything from elegant numbered steps to interactive checklists and feature lists. These techniques are essential for navigation menus, table of contents, pricing tables, and any content that benefits from structured, sequential presentation.

list-style-type Property

The list-style-type property controls the appearance of list markers. CSS provides a wide range of built-in marker styles for both unordered lists (bullets) and ordered lists (numbers, letters, or Roman numerals). This property is the simplest way to change list markers without requiring pseudo-elements.

Unordered List Markers

Unordered lists typically use bullet points. The three standard bullet styles are disc (filled circle, the default), circle (hollow circle), and square (filled square).

ul { list-style-type: disc; /* ● filled circle (default) */ } ul { list-style-type: circle; /* ○ hollow circle */ } ul { list-style-type: square; /* ■ filled square */ } ul { list-style-type: none; /* no marker */ }

Ordered List Markers

Ordered lists offer numerous numbering systems including decimal numbers, alphabetic characters, and Roman numerals. You can use lowercase or uppercase variants to match your design needs.

ol { list-style-type: decimal; /* 1, 2, 3 */ } ol { list-style-type: decimal-leading-zero; /* 01, 02, 03 */ } ol { list-style-type: lower-alpha; /* a, b, c */ } ol { list-style-type: upper-alpha; /* A, B, C */ } ol { list-style-type: lower-roman; /* i, ii, iii */ } ol { list-style-type: upper-roman; /* I, II, III */ }

list-style-position Property

The list-style-position property determines whether list markers appear inside or outside the list item's content box. This affects text wrapping behavior and is crucial for achieving the desired visual alignment.

Outside Position (Default)

With list-style-position: outside, markers are positioned outside the content box. When text wraps to multiple lines, subsequent lines align with the first line of text, not with the marker. This is the default behavior and typically provides the most readable layout.

Inside Position

With list-style-position: inside, markers are treated as part of the content. When text wraps, subsequent lines start at the same left edge as the marker, creating a different visual effect that can be useful in constrained layouts.

ul { list-style-position: outside; /* default */ } ul { list-style-position: inside; } /* Shorthand for both properties */ ul { list-style: square inside; }

Custom Markers with ::marker

The ::marker pseudo-element is a modern CSS feature that allows direct styling of list markers. You can change the marker content, color, font size, and font weight without removing the default list structure. This approach maintains semantic HTML while providing enhanced visual control.

Styling Default Markers

You can style existing markers by targeting the ::marker pseudo-element. This is particularly useful for changing the color or size of default bullets or numbers.

/* Style default markers */ li::marker { color: #2563eb; font-size: 1.2em; font-weight: bold; } /* Different colored numbers */ ol li::marker { color: #dc2626; font-weight: bold; }

Custom Marker Content

The true power of ::marker is the ability to replace default markers with custom content using the content property. You can use Unicode characters, emojis, or custom text.

/* Checkmark bullets */ .checklist li::marker { content: "✓ "; color: #16a34a; font-size: 1.2em; } /* Arrow bullets */ .steps li::marker { content: "➤ "; color: #2563eb; } /* Star bullets */ .featured li::marker { content: "★ "; color: #f59e0b; } /* Custom text markers */ .tasks li::marker { content: "→ "; color: #8b5cf6; font-weight: bold; }

Custom Bullets with ::before

The ::before pseudo-element provides maximum control over list markers but requires removing the default list styling. This technique involves setting list-style: none and creating custom markers with positioned pseudo-elements. It offers unlimited styling possibilities including gradients, shapes, and animations.

Basic Custom Bullets

To create custom bullets with ::before, remove the default list style, add relative positioning to list items, and create absolutely positioned pseudo-elements for your custom markers.

ul { list-style: none; } li { position: relative; padding-left: 30px; margin-bottom: 10px; } li::before { content: "→"; position: absolute; left: 0; color: #2563eb; font-weight: bold; font-size: 1.2em; }

Shape-Based Bullets

You can create geometric shapes as bullets using CSS. This technique is perfect for creating colored circles, squares, or triangles that match your design system.

/* Circle bullets */ li::before { content: ""; position: absolute; left: 0; top: 8px; width: 12px; height: 12px; border-radius: 50%; background: #2563eb; } /* Square bullets */ li::before { content: ""; position: absolute; left: 0; top: 8px; width: 12px; height: 12px; background: #16a34a; } /* Triangle bullets */ li::before { content: ""; position: absolute; left: 0; top: 8px; width: 0; height: 0; border-left: 6px solid transparent; border-right: 6px solid transparent; border-bottom: 12px solid #f59e0b; }

CSS Counters

CSS counters provide a powerful way to create custom numbering systems without relying on ordered lists. They work through three properties: counter-reset to initialize a counter, counter-increment to increase its value, and counter() function to display the current value. This system allows for complex numbering schemes with complete styling control.

Basic Counter Implementation

The fundamental pattern for CSS counters involves resetting a counter on the parent element, incrementing it on each child, and displaying the value in a pseudo-element.

ul { list-style: none; counter-reset: item; /* Initialize counter named 'item' */ } li { counter-increment: item; /* Increase counter by 1 */ } li::before { content: counter(item) ". "; /* Display counter value */ font-weight: bold; color: #2563eb; margin-right: 8px; }

Styled Counter Numbers

Since counters use pseudo-elements, you can apply any CSS styling including backgrounds, borders, positioning, and transforms. This enables you to create visually distinctive numbering systems.

ol { list-style: none; counter-reset: step; } li { counter-increment: step; position: relative; padding-left: 50px; margin-bottom: 15px; } li::before { content: counter(step); position: absolute; left: 0; top: 0; width: 35px; height: 35px; background: #2563eb; color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 1.1em; }

Counter Formatting

The counter() function accepts an optional second parameter to format the counter value using the same keywords as list-style-type.

/* Decimal with leading zeros */ li::before { content: counter(item, decimal-leading-zero) " "; /* Outputs: 01, 02, 03, ... */ } /* Lowercase alphabetic */ li::before { content: counter(item, lower-alpha) ") "; /* Outputs: a), b), c), ... */ } /* Roman numerals */ li::before { content: counter(item, upper-roman) ". "; /* Outputs: I., II., III., ... */ }

Nested List Styling

Nested lists create hierarchical relationships between items. CSS provides several approaches to differentiate between list levels, from using different marker types at each level to creating custom visual hierarchies with colors and indentation.

Progressive Marker Styles

A common pattern is to use different markers at each nesting level. This provides visual cues about the hierarchy without requiring complex styling.

/* Level 1: filled circle */ ul { list-style-type: disc; } /* Level 2: hollow circle */ ul ul { list-style-type: circle; margin-top: 8px; margin-left: 20px; } /* Level 3: square */ ul ul ul { list-style-type: square; }

Custom Nested Styling

For more sophisticated nested lists, use descendant selectors with custom markers and colors to create clear visual separation between parent and child items.

.nested-list { list-style: none; } /* Parent items */ .nested-list > li { position: relative; padding-left: 30px; margin-bottom: 10px; color: #2563eb; font-weight: 600; } .nested-list > li::before { content: "▼"; position: absolute; left: 5px; font-size: 0.8em; } /* Child items */ .nested-list ul { list-style: none; margin-top: 8px; } .nested-list ul li { padding-left: 25px; margin-bottom: 5px; color: #64748b; font-weight: normal; } .nested-list ul li::before { content: "•"; position: absolute; left: 35px; color: #cbd5e1; }

Modern List Designs

Beyond basic bullets and numbers, CSS enables you to transform lists into sophisticated UI components. Modern list designs include card-style layouts, interactive checklists, feature lists with icons, and step-by-step guides. These patterns are commonly used in pricing tables, onboarding flows, and feature showcases.

Card-Style Lists

Card-style lists treat each item as a distinct visual block with backgrounds, shadows, and hover effects. This design pattern works well for navigation, feature lists, and content previews.

.card-list { list-style: none; } .card-list li { padding: 15px; margin-bottom: 10px; background: white; border-left: 4px solid #2563eb; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); border-radius: 4px; transition: all 0.3s ease; } .card-list li:hover { transform: translateX(5px); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); }

Checklist Design

Checklists use checkbox-style markers to indicate completion status. While this example shows static styling, the pattern easily extends to interactive implementations with JavaScript.

.checklist { list-style: none; } .checklist li { position: relative; padding-left: 35px; margin-bottom: 12px; transition: color 0.3s ease; } .checklist li::before { content: "☐"; /* unchecked */ position: absolute; left: 0; font-size: 1.5em; color: #94a3b8; } .checklist li.checked { color: #94a3b8; text-decoration: line-through; } .checklist li.checked::before { content: "☑"; /* checked */ color: #16a34a; }

Feature List with Icons

Feature lists use consistent icon markers (typically checkmarks) within styled circles to highlight product features or benefits.

.feature-list { list-style: none; } .feature-list li { position: relative; padding: 12px 12px 12px 45px; margin-bottom: 10px; background: #f8fafc; border-radius: 8px; } .feature-list li::before { content: "✓"; position: absolute; left: 12px; top: 50%; transform: translateY(-50%); width: 24px; height: 24px; background: #16a34a; color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 0.9em; }

Step-by-Step Guide

Step lists combine CSS counters with styled layouts to create instructional content. They typically feature prominent step numbers and generous spacing for readability.

.step-list { list-style: none; counter-reset: step; } .step-list li { counter-increment: step; position: relative; padding: 20px 20px 20px 70px; margin-bottom: 20px; background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } .step-list li::before { content: "Step " counter(step); position: absolute; left: 20px; top: 20px; font-weight: bold; color: #2563eb; font-size: 0.85em; text-transform: uppercase; letter-spacing: 1px; } .step-list li::after { content: ""; position: absolute; left: 20px; top: 50%; width: 6px; height: 6px; background: #2563eb; border-radius: 50%; }

Key Takeaways