What are Tailwind CSS list utilities?
Each list class in Tailwind maps to a single declaration from the plain CSS list-style rules, so the class name tells you the output.
Marker type comes from list-disc, list-decimal, list-none and arbitrary values. Position is list-inside or list-outside. Images use list-image-[url(…)] or list-image-none, which is the one people tend to forget exists.
The utility-first Tailwind framework wraps those properties as classes, so a list gets styled in the markup instead of in a separate stylesheet. Anything the CSS properties can do, a class or an arbitrary value can do too.
There’s a limit, though. These classes style the list as a whole. Bullet color and size need the marker variant, and spacing between items comes from separate utilities such as space-y.
Why does Preflight remove bullets and numbers from lists?
Because Preflight sets list-style to none on ol, ul and menu elements, a list stays markerless until a utility brings the markers back.
Tailwind injects Preflight into the base layer when you import tailwindcss, according to the Tailwind CSS v4 documentation. It builds on modern-normalize, a stylesheet that smooths out cross-browser differences.
The documentation wording differs by version. The v3 page describes lists as having no bullets, numbers, margin or padding, while the v4 page shows the list-style rule alone.
In practice, pair list-disc with left padding such as pl-5. Outside markers need room to render.
How to set bullet and number styles with list-style-type
list-disc on a ul gives you bullets, and list-decimal on an ol gives you numbers. list-none removes the marker wherever you put it.
The class decides the marker, not the element. A list-disc on an ol prints bullets, and list-decimal on a ul prints numbers.
list-disc outputs list-style-type: disc, list-decimal outputs list-style-type: decimal, and list-none outputs list-style-type: none. That last one works best as the base of a responsive pair such as list-none md:list-disc, a pattern the Tailwind docs use.
Arbitrary and custom values
Square brackets handle one-off markers. A list-[upper-roman] class on an ol gives Roman numerals (I, II, III), and list-[square] gives square bullets. In v4, list-(–my-marker) reads the value from a CSS variable.
Tailwind CSS v3 projects register named values instead, such as square and roman, under theme.listStyleType in tailwind.config.js (Tailwind CSS v3 documentation).
A typo in the value produces a declaration the browser drops. The list then shows no marker at all, because the Preflight reset stays in effect.
list-inside or list-outside: which marker position fits?
Items that wrap across several lines want list-outside. Short items or narrow boxes are fine with list-inside.
MDN Web Docs lists outside as the initial value of list-style-position, so list-outside matches the browser default. In Tailwind, list-inside outputs list-style-position: inside and list-outside outputs list-style-position: outside.
With list-outside, wrapped lines line up with the first line of text (a hanging indent) and the markers stay clear of the text block. The cost is left padding, which you need once Preflight is active.
list-inside needs no extra padding, and the marker stays inside the list’s own box. Against that, wrapped lines run under the marker, you lose the hanging indent, and rendering gets inconsistent when the li starts with a block element.
That last case is documented. MDN says a block element placed first inside an li with list-style-position: inside goes on the line after the marker, while Safari keeps the marker on the same line as the block.
If your items start with a p or div, test in Safari before choosing list-inside. Plain text items avoid the problem.
How to change marker color and size with the marker variant
Add marker:text-sky-500 to the ul or ol, and every bullet or number takes that color while the item text keeps its own.
One placement is enough. Tailwind’s variant definition in the tailwindlabs/tailwindcss repository matches both the element’s own ::marker and the markers of its descendants, so the class works on the parent instead of every li.
The ::marker pseudo-element accepts color, all font properties, content, the counter properties (counter-increment, counter-reset, counter-set), quotes, white-space, direction, unicode-bidi, text-combine-upright, and animation and transition properties (MDN Web Docs). Background-color and margin are not on that list, so filled or padded bullets need a different method.
Safari narrows the list further. Compatibility data from Can I Use and MDN limits Safari’s ::marker support to color and font-size.
Stick to color and font-size if your audience includes Safari.
Markers are rendered text, so they need enough color contrast against the background. WCAG 2.2 success criterion 1.4.3 sets 4.5:1 for normal text and 3:1 for large text (W3C).
How to use custom bullets and icons in a Tailwind list
list-image handles a static icon, while inline SVG is the pick for anything you need to size or recolor. A ::before glyph works for plain text symbols.
| Method | Size control | Recolor | Extra markup |
|---|
| list-image-[url(…)] | None, image keeps its own size | Edit the image file | None |
| before:content-[‘+’] on li | Font size utilities | Text color classes | None, needs padding |
| Inline SVG inside li | size-* classes | text-* classes | One svg per item |
The Tailwind docs demo list-image with list-image-[url(/img/checkmark.png)] on the ul. In v4, list-image-(–my-list-image) works as shorthand for list-image-[var(–my-list-image)].
The catch is sizing. list-style-image uses the picture as the marker itself, so CSS cannot resize or filter it directly.
When the icon has to match your palette, inline SVG markup from Heroicons is the cleaner route. The set ships as 24 pixel outline and solid icons, plus 20 and 16 pixel solid icons, and the icons take their color from the CSS color property (Tailwind Labs, Heroicons 2.2.0).
Put list-none on the ul and flex items-start gap-2 on each li. The flex class replaces the li’s list-item display, so the item shows no marker of its own.
A before:content-[‘+’] class on each li draws a text symbol instead. Tailwind’s content utilities are built for the before and after variants, and MDN discourages meaningful content inside pseudo-elements because screen readers do not reliably announce it.
My rule of thumb is simple. If the bullet changes color or size anywhere, inline SVG wins. If it never changes, list-image costs less markup.
How to space, indent and nest list items
Padding on the list handles indentation, and sibling margins handle the space between items. Nested levels each need their own marker type again.
Indentation
Left padding such as pl-5 or pl-6 on the ul or ol gives list-outside markers room to render. Left margin (ml-*) shifts the list box itself, so a background or border on the ul moves with it. list-inside removes the need for either, at the price of the hanging indent.
Item spacing
Space on the ul, not on every li.
The space-y-* classes add vertical margin between sibling items and leave the markers untouched.
space-y-2 on the ul is a single class and works with list-disc and list-decimal. Putting mb-2 on each li gives the same visual result, but it’s repeated on every item and the last one gets a bottom margin too. For lists without markers, such as icon lists and menus, gap-* on a flex or grid ul is the better fit.
Nested lists
Place the inner ul inside its parent li, not beside it. Each level then needs its own marker class and its own indent.
- Level one: list-disc pl-5
- Level two: list-[circle] pl-5
- Level three: list-[square] pl-5
Past three levels a list stops reading as a list.
Horizontal lists with Flexbox
Put flex, flex-wrap and gap-4 on the ul, and each li becomes an item in a row.
Skip list-none unless another class or a wrapper brings markers back. The base styles already strip them.
How do responsive, state and dark mode variants work on lists?
Prefix any list utility with a variant, and the class applies only under that condition, whether that’s a breakpoint, a state such as hover, or dark mode.
md:list-image-[url(/img/checkmark.png)] shows the image marker from medium screens up (Tailwind CSS documentation). hover:list-image-[url(checkmark.png)] swaps the marker on hover, and dark:marker:text-sky-400 recolors the markers in dark mode.
Breakpoint prefixes follow a mobile-first design logic. The unprefixed class sets the small-screen style, and the prefixed class takes over from that breakpoint up.
By default the dark variant follows the operating system through the prefers-color-scheme media query, according to the Tailwind CSS v4 documentation. For a manual toggle, override it in your stylesheet with @custom-variant dark (&:where(.dark, .dark *)).
Tailwind CSS v3 handles the same switch through darkMode: ‘selector’ in tailwind.config.js. The selector strategy replaced the older class strategy in v3.4.1.
Image markers do not recolor with the theme. Give each mode its own file instead, for example list-image-[url(/img/checkmark.png)] dark:list-image-[url(/img/checkmark-light.png)].
What changed for lists between Tailwind CSS v3 and v4?
The list class names are the same in both versions. What changed is import syntax, custom value handling, the spacing selector and the browser floor.
| Area | Tailwind CSS v3 | Tailwind CSS v4 |
|---|
| Import | @tailwind base, components, utilities | @import “tailwindcss” |
| Custom marker images | theme.listStyleImage in tailwind.config.js | list-image-(–my-list-image) shorthand |
| space-y selector | > :not([hidden]) ~ :not([hidden]), margin-top | > :not(:last-child), margin-bottom |
| Browser floor | Older browsers still covered | Safari 16.4, Chrome 111, Firefox 128 |
The selector change is why space-y spacing on a list now lands on every item except the last. Tailwind’s upgrade guide even swaps space-y-4 for flex flex-col gap-4 in its own migration example.
Sources disagree on list images. Some older tutorials say Tailwind has no list-style-image utility.
The v3 documentation settles it. It already lists list-image-none, arbitrary list-image-[url(checkmark.png)] values and the theme.listStyleImage key.
If older browsers are on your list, check your cross-browser compatibility requirements before upgrading. The upgrade guide says to stay on v3.4 until your browser support requirements change.
Why are Tailwind list classes not working?
Most of the time the list has no marker class on itself, the li has a display other than list-item, Tailwind never generated the class name, or the HTML comes from somewhere you don’t control. The fixes below follow that order.
- Put list-disc or list-decimal on the ul or ol, not on a wrapper div.
- Check the li display. MDN documents that ::marker only exists on elements with display: list-item, so flex, grid or block classes on the li remove the bullet.
- Check class detection. Tailwind scans source files as plain text, so a string like list-${type} never exists as a full class name and is not generated. Map values to complete class names, or force generation with @source inline() (Tailwind CSS v4 documentation).
- For Markdown or CMS output, wrap the content in prose or add an arbitrary variant such as [&_ul]:list-disc on the wrapper.
The @tailwindcss/typography plugin styles ordered and unordered lists along with headings and tables, and not-prose opts a block back out (Tailwind CSS documentation). In v4 you register it with @plugin “@tailwindcss/typography” in your CSS.
Syncfusion documents this exact collision. Its Rich Text Editor shows lists without bullets or numbers when Preflight is loaded, and its Tailwind Preflight guide fixes it with !important overrides on ul and ol.
When a class on an item still loses, a CSS specificity calculator shows which selector wins. Fix the weaker rule instead of stacking !important.
Tailwind CSS List Utilities FAQ
Does list-none hurt screen reader accessibility?
Yes, in Safari with VoiceOver. WebKit treats a list without visible markers as a layout list and skips the list announcement, so add role=”list” to the ul. Rocket Validator flags that role as redundant, but the warning is safe to accept.
Can you turn off Preflight for lists only?
Not with a single switch. Omitting the Preflight import removes every reset, not just the list rule, and v3 behaves the same through corePlugins: { preflight: false }.
For lists alone, put your own rule in @layer base, such as ul { list-style-type: disc; padding-left: 1.25rem; } (Tailwind CSS documentation).
Does the marker variant recolor a list-image bullet?
No. The marker variant sets color on the ::marker box, which colors text bullets and numbers. An image from list-image keeps its own colors, so swap the file or switch to inline SVG, whose icons take color from text-* classes.
Are ready-made Tailwind list components worth using?
Rarely for plain bullets, since a ul with list-disc and pl-5 is shorter than any component.
Libraries pay off for richer rows. Material Tailwind ships list examples with profile pictures and badges. Tailwind Plus (formerly Tailwind UI), the paid component library from Tailwind Labs, closed to new customers in September 2026 when Tailwind Labs joined Shopify, although existing customers keep their access.
Where List Styles Belong in a Tailwind CSS Project
Site-wide defaults go in an @layer base rule. Repeated icon rows go in a shared component, and utility classes on the list handle the one-offs.
Work through them in this order, because the base layer reaches the widest surface first. Markdown and CMS output cannot carry per-list classes, so only a wrapper or base-level rule styles it.
- Base rule for ul and ol defaults
- Shared component for icon lists
- Utility classes for exceptions
The trade-off is menus. A base rule that restores bullets restores them on every navigation ul too, so each menu needs list-none, and a navbar built with Tailwind is the natural place to see that cost.
Verified against the Tailwind CSS v4.3 documentation in October 2026. A change to Preflight’s list rule would reverse the first step.