CSS list-style Properties
Every <ul> and <ol> element renders a marker next to each <li> — a bullet, a number, or an image — and the CSS list-style family of properties controls exactly what that marker looks like and where it sits. Left alone, browsers apply sensible defaults (a disc for unordered lists, decimal numbers for ordered lists), but real designs almost always need custom bullets, no bullets at all, or markers repositioned relative to the list text. Understanding these properties — and their quirky interaction with margins, padding, and the newer ::marker pseudo-element — is essential for building clean navigation menus, styled bullet points, and readable numbered content.
Overview / How it works
Every list item generates a marker box in addition to its principal content box. The marker box holds the bullet or number, and the list-style-* properties decide what goes in it and how it relates to the content box. There are three longhand properties — list-style-type, list-style-image, and list-style-position — plus a list-style shorthand that sets all three at once.
These properties are only meaningful on elements with display: list-item — by default that’s <li> — but they are also inherited, so it’s conventional to set them once on the <ul> or <ol> and let every <li> inside pick them up through inheritance rather than repeating them on each item. This inheritance is why a rule like ul { list-style-type: none; } removes bullets from every item in that list without touching the items directly.
The rendering engine treats the marker box differently depending on list-style-position. With the default outside value, the marker sits in the list item’s margin area, outside the content box, so it doesn’t affect text wrapping. With inside, the marker becomes part of the content box’s first line, as if it were inline content prepended to the item — this means wrapped text on a second line will align with the left edge of the list item, not with the first line of text, which is a common source of confusion in multi-line list items.
It’s also important to know that list-style-type: none only removes the marker — it does not remove the default left padding (usually 40px in browser stylesheets) or the top/bottom margin that user-agent stylesheets apply to <ul>/<ol>. Many developers expect list-style: none alone to produce a fully unstyled list and are surprised by the leftover indentation; you must reset margin and padding separately.
Syntax
list-style-type: disc | circle | square | decimal | decimal-leading-zero |
lower-roman | upper-roman | lower-alpha | upper-alpha | none | <custom-ident>;
list-style-image: none | url("marker.svg");
list-style-position: outside | inside;
list-style: <type> <position> <image>;
| Property | Purpose | Initial value |
|---|---|---|
list-style-type |
Chooses the marker’s counter style or symbol (bullet shape or numbering scheme) | disc |
list-style-image |
Replaces the marker with an image, overriding list-style-type when it loads successfully |
none |
list-style-position |
Places the marker box inside or outside the content box | outside |
list-style |
Shorthand for all three properties above, in any order | — |
Common list-style-type keywords worth knowing beyond disc/circle/square: decimal-leading-zero (01, 02, 03…), lower-roman/upper-roman (i, ii, iii… / I, II, III…), lower-alpha/upper-alpha (a, b, c… / A, B, C…), and none (no marker at all). CSS also supports the @counter-style at-rule for fully custom numbering systems, though that’s an advanced topic beyond this lesson.
Examples
Example 1: Changing the bullet shape
ul.tasks {
list-style-type: square;
color: #333;
}
Result: Every item in a <ul class=”tasks”> shows a small solid square marker instead of the default round disc. The text itself stays the same dark grey color; only the bullet shape changes.
This is the simplest way to swap the built-in marker shape. Because list-style-type is inherited, you only need to declare it once on the <ul>, and it flows down to every <li> automatically.
Example 2: Using an image as the marker
ul.feature-list {
list-style-image: url("check-icon.svg");
list-style-position: outside;
padding-left: 1.5rem;
}
ul.feature-list li {
margin-bottom: 8px;
line-height: 1.4;
}
Result: Each item displays a small checkmark icon (from check-icon.svg) in place of the bullet, sitting in the outside margin area to the left of the text, with 8px of vertical spacing separating each item.
Using list-style-image lets you brand a list with a custom icon rather than a plain shape. Because image markers can render inconsistently in size and vertical alignment across browsers, many developers prefer a more controllable alternative: setting list-style: none and adding a background image or an ::before pseudo-element on the <li> instead — that approach gives pixel-perfect control over icon size and position.
Example 3: Custom numbering with the shorthand and styled markers
ol.steps {
list-style: upper-roman inside;
counter-reset: none;
}
ol.steps::marker {
color: #d94f04;
font-weight: bold;
}
Result: The ordered list numbers items with bold, orange-colored uppercase Roman numerals (I., II., III., …), and because the position is inside, the numeral sits flush with the item’s text as if it were the first word, so wrapped second lines align under the numeral rather than under the first line of text.
This example combines the list-style shorthand (type + position in one declaration) with the modern ::marker pseudo-element, which is the standards-based way to style marker color, font, and weight without affecting the item’s text color. Before ::marker existed, developers had to set color on the whole <li>, which unintentionally recolored the text too.
How it works step by step
When the browser lays out a list, it performs roughly these steps for each <li>:
- It generates a marker box based on the item’s computed
list-style-type,list-style-image, and any active::markerstyles. - If
list-style-imageresolves to a valid image, that image is used as the marker content; otherwise the engine falls back to renderinglist-style-type. - It checks
list-style-position:outsideplaces the marker box in the list item’s margin/padding area (not counted as part of the content box’s inline flow), whileinsideplaces it at the very start of the content box’s first line, so it participates in text flow and wrapping. - For ordered lists, the engine also maintains an implicit counter that increments for each sibling <li>, formatting the counter value according to the numbering system named in
list-style-type(decimal, roman numerals, alphabetic, etc.). - Finally, any
::markerpseudo-element rules are applied to style the marker’s limited set of stylable properties (color, font properties,content, white-space) independently from the rest of the item.
Common Mistakes
Mistake 1: Assuming list-style: none removes indentation.
ul.nav {
list-style: none
}
This is missing a semicolon and, more importantly, leaves the browser’s default padding-left (often 40px) and vertical margin untouched, so the list still appears indented even though bullets are gone.
ul.nav {
list-style: none;
margin: 0;
padding: 0;
}
The corrected version explicitly zeroes out margin and padding, producing a truly flush, bullet-free list — the standard reset for building horizontal nav menus from <ul> elements.
Mistake 2: Styling the marker color by setting color on the <li>.
li { color: red }
Because this rule has no property value terminator and, worse, recolors the entire line of text (not just the bullet), readers get red body text when the intent was only a red bullet.
li {
color: inherit;
}
li::marker {
color: red;
}
Targeting ::marker directly styles only the bullet or number, leaving the item’s text in its normal color — this is the correct, modern approach supported in all current evergreen browsers.
Best Practices
- Set
list-style-type/list-styleon the <ul> or <ol> itself and rely on inheritance rather than repeating it on every <li>. - When removing bullets for a navigation menu, always reset
marginandpaddingalongsidelist-style: none. - Prefer
::markerover recoloring the whole <li> when you only want to style the bullet or number. - Be cautious with
list-style-imagefor icon bullets — sizing and vertical alignment vary by browser; a background-image or::beforepseudo-element on the <li> often gives more precise control. - Use
list-style-position: insideonly when you want the marker to participate in text wrapping (e.g., numbered steps with hanging indentation), since it changes how wrapped lines align. - Always provide a semantically appropriate list element (<ul> or <ol>) even when markers are hidden with
list-style: none— screen readers still announce list semantics, which benefits accessibility.
Practice Exercises
- Create a <ul> representing a horizontal navigation bar. Remove its bullets and default spacing, then use Flexbox to lay its <li> items out in a row.
- Build an <ol> styled with
list-style-type: decimal-leading-zeroand use::markerto make the numbers bold and a different color from the item text. - Take a <ul> with
list-style-imagepointing at a small icon, and compare how it looks withlist-style-position: outsideversusinsidewhen an item’s text wraps onto two lines.
Summary
list-style-typecontrols the bullet shape or numbering scheme;list-style-imagereplaces it with a picture;list-style-positiondecides whether the marker sits outside or inside the content box.list-styleis the shorthand combining all three, acceptable in any order.- These properties inherit, so declare them once on the <ul>/<ol> rather than on each <li>.
list-style: noneremoves only the marker — margin and padding must be reset separately for a fully flush list.- The
::markerpseudo-element is the modern, precise way to style marker color and font without affecting the item’s own text.
