Styling HTML Lists with CSS
Browsers give every list the same plain look: round bullets, plain numbers, and a wide left indent. It is clear and readable, but it is rarely what a finished design needs. A menu should have no bullets. A checklist might want check marks. A "top 10" might want numbers like 01, 02, 03.
The good news: you never need to change your HTML to do any of this. The list stays a list. CSS only changes how it looks. In this lesson, you will learn the properties that control markers, position, spacing, and layout.
What You Will Learn
- How to change bullet and number styles with
list-style-type - How to control marker position and use images as markers
- How to style markers with the
::markerpseudo-element - How to remove bullets, and what to watch out for
- How to build horizontal lists and menus
- How to space, indent, and number lists for a global audience
This lesson uses basic CSS. If properties and selectors are new to you, you can still follow along. Every example shows the result.
The Parts of a List Item
Each <li> in a list has two parts:
- The marker: the bullet, number, or letter.
- The content: the text or elements inside the item.
Most list properties control the marker. Others control the box around the whole list.
| Part | Properties |
|---|---|
| Marker | list-style-type, list-style-image, list-style-position, ::marker |
| Whole list | padding, margin, display, gap |
| Shorthand | list-style |
list-style-type: Choose the Marker
This property sets the marker's shape or numbering style.
For Bullet Lists
| Value | Marker |
|---|---|
disc | Solid circle (default) |
circle | Hollow circle |
square | Solid square |
none | No marker |
For Numbered Lists
| Value | Marker |
|---|---|
decimal | 1, 2, 3 (default) |
decimal-leading-zero | 01, 02, 03 |
lower-alpha | a, b, c |
upper-alpha | A, B, C |
lower-roman | i, ii, iii |
upper-roman | I, II, III |
lower-greek | α, β, γ |
- HTML + CSS
- Browser Output
<ul style="list-style-type: square;">
<li>Square</li>
<li>Markers</li>
</ul>
<ul style="list-style-type: circle;">
<li>Hollow</li>
<li>Circles</li>
</ul>
<ol style="list-style-type: decimal-leading-zero;">
<li>First</li>
<li>Second</li>
<li>Third</li>
</ol>
<ol style="list-style-type: upper-roman;">
<li>Plan</li>
<li>Build</li>
<li>Launch</li>
</ol>
- Square
- Markers
- Hollow
- Circles
- First
- Second
- Third
- Plan
- Build
- Launch
In a real project, put the styles in a stylesheet, not in a style attribute:
ul.features {
list-style-type: square;
}
ol.steps {
list-style-type: decimal-leading-zero;
}
Targeting every ul on the page changes menus, footers, and everything else. Give your styled lists a class (.features, .steps) so changes stay where you want them.
list-style-position: Inside or Outside?
This sets where the marker sits compared to the text block.
| Value | Meaning |
|---|---|
outside (default) | The marker hangs in the margin. Wrapped lines align with the text. |
inside | The marker sits inside the text box. Wrapped lines go back under the marker. |
- HTML + CSS
- Browser Output
<ul style="list-style-position: outside;">
<li>Outside: when this item is long enough to wrap, the second line lines up under the first word of the text.</li>
</ul>
<ul style="list-style-position: inside;">
<li>Inside: when this item is long enough to wrap, the second line begins under the bullet itself.</li>
</ul>
- Outside: when this item is long enough to wrap, the second line lines up under the first word of the text.
- Inside: when this item is long enough to wrap, the second line begins under the bullet itself.
outside is easier to read for multi-line items, which is why it is the default. Use inside when you want the marker to be part of a centered or boxed item.
list-style-image: Use a Picture as the Marker
You can replace the bullet with an image, such as a small check mark or arrow.
ul.checklist {
list-style-image: url("images/check.svg");
}
This is simple, but limited. You cannot easily resize or recolor the image, and its vertical alignment can look off. For more control, use the techniques in the next sections.
::marker: Style the Marker Itself
The ::marker pseudo-element targets the bullet or number only, not the text. You can change its color, size, and font.
li::marker {
color: #2563eb;
font-weight: 700;
}
ol.steps li::marker {
font-size: 1.2em;
}
With this, the bullets turn blue while the text stays the same color. It is the easiest way to add a splash of style.
::marker can changeIt supports a limited set of properties: color, font-* properties, content, and a few others. It cannot change spacing, background, or size of the marker box. For those, use the custom marker technique below.
Custom Markers with content
Because ::marker accepts the content property, you can set any text or emoji as the marker.
ul.checklist li::marker {
content: "✓ ";
color: #16a34a;
}
ul.arrows li::marker {
content: "→ ";
}
Browser support for content in ::marker is good in modern browsers. For full control and the oldest browsers, you can remove the marker and draw your own with ::before:
ul.custom {
list-style: none;
padding-left: 0;
}
ul.custom li {
position: relative;
padding-left: 1.5rem;
}
ul.custom li::before {
content: "★";
position: absolute;
left: 0;
color: #f59e0b;
}
Here is a live example of the idea, using real characters as the markers:
- Free to learn
- Works on any device
- No setup needed
Marker text added with CSS is not part of your HTML. Some screen readers read it, and some do not. Use CSS markers for decoration, like ticks and stars. Never use them for information that must not be missed, such as "Warning" or "Required."
The list-style Shorthand
You can write the three marker properties in one line:
/* list-style: type position image */
ul {
list-style: square inside;
}
/* Remove everything */
ul.plain {
list-style: none;
}
Values can appear in any order, and you can leave some out.
Removing Bullets: The Right Way
Menus, tag lists, and cards usually look wrong with bullets. To remove them, you need to reset two things: the marker and the default left indent.
- HTML + CSS
- Browser Output
ul.plain {
list-style: none;
margin: 0;
padding: 0;
}
<ul class="plain">
<li>No bullets</li>
<li>No left indent</li>
</ul>
- No bullets
- No left indent
Many beginners write only list-style: none and wonder why the text still has a gap on the left. That gap is the list's padding-inline-start (about 40px). Reset it as well.
In some browser and screen reader combinations (notably Safari with VoiceOver), a list with list-style: none is no longer announced as a list. If the content really is a list, such as a navigation menu, add role="list" to the <ul>:
<ul class="plain" role="list">
You will learn more about this in Accessible Lists.
Horizontal Lists and Menus
A navigation bar is just a list laid out in a row. The two classic tools are Flexbox and inline/inline-block items. Flexbox is the modern choice.
- HTML + CSS
- Browser Output
.menu {
display: flex;
gap: 1.5rem;
list-style: none;
margin: 0;
padding: 0;
}
<nav aria-label="Main">
<ul class="menu">
<li><a href="/">Home</a></li>
<li><a href="/courses">Courses</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
The HTML did not change at all. It is still a plain list of links, and screen readers still treat it that way. Only the layout changed.
A Wrapping "Tag" List
With flex-wrap, items flow onto new lines when the space runs out. This suits tags and chips.
- HTML + CSS
- Browser Output
.tags {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
list-style: none;
margin: 0;
padding: 0;
}
.tags li {
background: #e0e7ff;
color: #1e3a8a;
padding: 0.25rem 0.75rem;
border-radius: 999px;
}
<ul class="tags">
<li>HTML</li>
<li>CSS</li>
<li>JavaScript</li>
<li>Accessibility</li>
</ul>
- HTML
- CSS
- JavaScript
- Accessibility
Spacing and Indentation
| Goal | CSS |
|---|---|
| Change the indent | padding-inline-start: 1.5rem; |
| Remove the indent | padding-inline-start: 0; |
| Space between items | li + li { margin-top: 0.5rem; } or gap on a flex or grid list |
| Space around the list | margin-block: 1rem; |
| Tighter line height | line-height: 1.4; on li |
- HTML + CSS
- Browser Output
.roomy li + li {
margin-top: 0.75rem;
}
<ul class="roomy">
<li>Comfortable spacing</li>
<li>Between each item</li>
<li>Is easier to read</li>
</ul>
- Comfortable spacing
- Between each item
- Is easier to read
Notice padding-inline-start rather than padding-left. Logical properties like this follow the page's writing direction, so they work correctly in right-to-left languages such as Arabic and Hebrew without extra CSS.
Lists in Other Writing Systems
Not every reader counts with 1, 2, 3. CSS can show numbers in many scripts.
| Value | Script | Looks like |
|---|---|---|
arabic-indic | Arabic-Indic digits | ١، ٢، ٣ |
devanagari | Devanagari digits | १, २, ३ |
bengali | Bengali digits | ১, ২, ৩ |
thai | Thai digits | ๑, ๒, ๓ |
cjk-ideographic | Chinese/Japanese numerals | 一, 二, 三 |
hiragana | Japanese Hiragana letters | あ, い, う |
- HTML + CSS
- Browser Output
<ol style="list-style-type: devanagari;">
<li>पहला चरण</li>
<li>दूसरा चरण</li>
<li>तीसरा चरण</li>
</ol>
- पहला चरण
- दूसरा चरण
- तीसरा चरण
If a script you need is not on the list, the CSS @counter-style rule lets you define your own numbering system. That is an advanced topic, but it is good to know it exists.
Set lang on the page (or on the list, as above), and use the numbering your readers expect. Browsers pick better fonts and spacing when they know the language.
Putting It Together: A Styled Checklist
- HTML + CSS
- Browser Output
.card-list {
list-style: none;
margin: 0;
padding: 0;
max-width: 24rem;
}
.card-list li {
padding: 0.75rem 1rem;
border: 1px solid #cbd5e1;
border-radius: 8px;
}
.card-list li + li {
margin-top: 0.5rem;
}
<ul class="card-list" role="list">
<li>Read the lesson</li>
<li>Try the example</li>
<li>Finish the practice</li>
</ul>
- Read the lesson
- Try the example
- Finish the practice
Quick Reference
| Goal | Use |
|---|---|
| Change bullet shape | list-style-type: square; |
| Change number style | list-style-type: upper-roman; |
| Zero-padded numbers | list-style-type: decimal-leading-zero; |
| Color the markers | li::marker { color: ... } |
| Custom marker text | li::marker { content: "✓ "; } |
| Remove bullets | list-style: none; margin: 0; padding: 0; |
| Keep list meaning | add role="list" after removing bullets |
| Horizontal list | display: flex; gap: ...; |
| RTL-friendly indent | padding-inline-start |
Common Mistakes to Avoid
- Removing bullets but not the padding. Reset both
list-styleandpadding. - Styling every
ulon the page. Use a class, so menus and footers are not affected by accident. - Using
display: inlineon<ul>to make it horizontal. Putdisplay: flexon the<ul>, or style the<li>items. - Putting important words in CSS markers. Screen readers may skip them.
- Using
list-style: noneon a real list without thinking about screen readers. Addrole="list"when it matters. - Faking bullets with characters in the HTML instead of using CSS. It breaks list semantics.
- Using
padding-leftin multilingual sites. Preferpadding-inline-start. - Changing the HTML to change the look. Switching
<ol>to<ul>just for numbers or bullets changes the meaning. Use CSS.
Practice Time
Try these in your editor:
- Make a bullet list with square markers, then hollow circles.
- Style a numbered list with
decimal-leading-zero. - Turn the markers blue with
li::marker. - Set a check mark as the marker using
content. - Compare
insideandoutsideon an item long enough to wrap. - Remove the bullets and the left indent from a list of links.
- Turn that list into a horizontal menu with Flexbox.
- Build a row of rounded "tags" that wrap on small screens.
- Add
role="list"to a list whose bullets you removed. - Bonus: show a three-step list with the numbering of another writing system.
Quick Recap
Click a question to check your answer.
1. Which property changes the shape or numbering style of the marker?
list-style-type, with values such as square, circle, decimal-leading-zero, and upper-roman.
2. How do you change only the color of the bullets?
Use the ::marker pseudo-element, for example li::marker { color: blue; }.
3. What two things should you reset to make a clean, bullet-free list?
list-style: none removes the marker, and padding (or padding-inline-start) removes the left indent.
4. How do you turn a list into a horizontal menu?
Put display: flex (with a gap) on the <ul>. The HTML stays the same.
5. Why might you add role="list" after removing bullets?
Some browser and screen reader combinations stop announcing a list when list-style: none is applied. The role keeps its meaning.
Key Takeaways
- CSS changes how a list looks. The HTML keeps its meaning.
list-style-type,list-style-position, andlist-style-imagecontrol markers, and::markerstyles them.- For clean menus, remove both the bullets and the default padding, and add
role="list"when the list still matters. - Flexbox turns any list into a row, a wrap of tags, or a card stack.
- Use logical properties and the right numbering system so lists work for readers everywhere.
Your lists now look the way you want, and they should work for everyone too. The last lesson in this section covers just that: Accessible Lists. You can also return to the Lists overview.