An HTML list, <ul> or <ol>, ships with default bullets or numbers, a browser style that almost never matches a modern design. The list-style property, together with its trio of detailed sub-properties, lets you take over everything: the symbol, its position, or a custom image.
It shows up constantly in one specific case: the navigation bar. A nav is almost always built as a list, for semantic reasons, then stripped of its bullets in CSS to look like a row of links rather than a bulleted list.
Definition
list-style is a shorthand that combines three properties: list-style-type, the symbol used, list-style-position, where the symbol sits relative to the text, and list-style-image, an image instead of the default symbol. It applies to <ul>, <ol> elements, and directly to each <li> when finer control is needed.
The three properties in detail
ul {
list-style-type: square;
list-style-position: inside;
list-style-image: none;
}
/* equivalent in a single line */
ul {
list-style: square inside;
}| Property | Role | Common values |
|---|---|---|
list-style-type | The symbol or numbering style | disc, circle, square, decimal, none |
list-style-position | The symbol inside or outside the text box | outside (default), inside |
list-style-image | An image used as the bullet | url(...), none |
list-style-position deserves special attention: with the default outside value, the bullet sits outside the text box, which slightly offsets the alignment of a multi-line item compared to its first character. With inside, the bullet becomes part of the text flow, which changes the indentation of the following lines.
Customizing a bullet with an image or generated content
list-style-image accepts an image but offers little control over its size or alignment. In practice, many projects prefer disabling list-style and drawing the bullet themselves with the content property on a pseudo-element, which gives far more freedom.
ul.checklist {
list-style: none;
padding-left: 0;
}
ul.checklist li {
padding-left: 1.5em;
position: relative;
}
ul.checklist li::before {
content: "✓";
position: absolute;
left: 0;
color: green;
}Classic trap: list-style: none and accessibility
Removing bullets with list-style: none is purely visual: it changes nothing about the document's structure, which is still a list. The real trap lies elsewhere. Some browsers, Safari in particular, also drop the semantic list role announced to screen readers as soon as a list has no bullet style left, which can make it read as a plain series of links instead of a navigation list.
role="list" on the <ul> or <ol> in question in addition to list-style: none. This forces the semantic role to stay present, regardless of the browser or screen reader in use.Complete example: a horizontal navigation
The most common use of list-style: none remains the navigation bar. A nav built as a list keeps its meaning for accessibility, while displaying as a row of links thanks to a few lines of CSS.
nav ul {
list-style: none;
display: flex;
gap: 1.5rem;
padding-left: 0;
margin: 0;
}
nav ul li a {
text-decoration: none;
font-weight: 600;
}In a larger project, the classes for this kind of component often follow a convention like BEM, for example nav__list and nav__item, rather than generic selectors like ul and li, to avoid a style meant for this navigation accidentally applying to another list on the page.
FAQ
list-style: none also remove a list's default indentation?No, these two behaviors are independent. A list's default margin and padding come from other properties, which must be removed separately with margin: 0 and padding: 0 for a fully flat alignment.
Yes, with a declaration such as list-style-type: "🚀 ", placing a quoted string directly as the value, a syntax supported by modern browsers to replace the classic symbols.
Yes, but that setting goes through the HTML start attribute on the <ol> tag, or the CSS counter-reset property combined with a custom counter, rather than through list-style itself, which only controls the symbol's appearance.