Styling a Dotted World Map SVG with Tailwind CSS

Tailwind CSS styles SVG elements a little differently than it styles a <div>, and a dotted world map exposes that difference immediately: fill and stroke colors, hover states on individual dots, and dark mode all need slightly different handling than the utility classes you're used to reaching for elsewhere in a project. Generate a dotted world map first if you don't already have a source SVG to work from.
Getting the SVG Into a Tailwind-Stylable Shape
Tailwind's utility classes only work on elements you can actually put a class attribute on. If your map is a static .svg file loaded through an <img> tag, Tailwind can't reach inside it — the SVG's internal paths and circles are invisible to your stylesheet. To style individual dots or countries, the SVG needs to be inline in your HTML/JSX, not referenced as an image source.
Once it's inline, Tailwind's fill and stroke utilities work directly on the SVG elements:
<svg viewBox="0 0 1000 600" className="w-full h-auto">
<circle className="fill-slate-400 dark:fill-slate-600" cx="120" cy="80" r="2" />
<circle className="fill-slate-400 dark:fill-slate-600" cx="130" cy="82" r="2" />
</svg>
Tailwind ships fill-* and stroke-* utilities that map to the same color palette as bg-* and text-*, so a map's dot color can pull from the exact same design tokens as the rest of your UI — no separate color system to maintain.
Dark Mode
If your project uses Tailwind's dark: variant (class-based or media-based), apply it directly to the fill and stroke utilities rather than wrapping the whole map in a separate dark-mode SVG export:
<circle className="fill-slate-800 dark:fill-slate-300" ... />
This is a meaningfully different approach from generating two separate map exports for light and dark themes, and it's the one that scales — one SVG, two color states, no duplicate assets to keep in sync. If you're deciding on the actual colors to use for each mode rather than just the mechanics of the toggle, how to choose map colors for dark mode designs is worth reading alongside this.
Hover and Interactive States
Tailwind's hover: variant works on SVG shapes the same way it works on buttons:
<path
className="fill-slate-300 hover:fill-blue-500 transition-colors cursor-pointer"
d="..."
/>
For a "highlight the whole region on hover" effect where hovering one element should affect siblings, use Tailwind's group and group-hover utilities, wrapping each region in its own <g className="group">:
<g className="group">
<path className="fill-slate-300 group-hover:fill-blue-500" d="..." />
<circle className="fill-slate-400 group-hover:fill-blue-600" cx="10" cy="10" r="2" />
</g>
This pairs naturally with the interaction patterns described in adding interactive hover effects to your SVG maps — the difference here is purely mechanical: Tailwind utilities instead of hand-written CSS classes, with the same visual result.
The Purge/Content-Scanning Trap
This is the mistake that actually breaks Tailwind maps in production, not in dev. Tailwind only generates the CSS for classes it can find as literal strings in your source files during its build-time content scan. If you build class names dynamically — for example, computing a fill color from a data value at runtime — Tailwind won't know to generate that class, and the style will silently fail to apply after a production build even though it worked fine in local dev with the JIT engine watching every keystroke.
// This breaks in production — Tailwind never sees "fill-red-500" as a literal string
<circle className={`fill-${riskLevel === "high" ? "red" : "green"}-500`} />
Fix it by using complete, literal class names and letting a ternary or lookup object choose between them, or by adding the specific classes you generate dynamically to Tailwind's safelist in tailwind.config.js:
// This works — both full class names exist literally in the source
<circle className={riskLevel === "high" ? "fill-red-500" : "fill-green-500"} />
If you're conditionally coloring dots based on data (population, revenue tier, risk level), and you have more than a handful of possible values, a safelist entry is usually cleaner than writing out every combination as a ternary.
Arbitrary Values for Brand Colors
If your map needs to match an exact brand hex code that isn't in Tailwind's default palette, arbitrary value syntax avoids extending the theme just for one-off cases:
<circle className="fill-[#2c5eff]" ... />
For a color used repeatedly across a project, it's still worth adding it to theme.extend.colors in your Tailwind config instead, so you get a real utility name (fill-brand-blue) rather than a hex code scattered through your components.
Final Thoughts
Tailwind doesn't need any special plugin to style an SVG map — fill-*, stroke-*, hover:, group-hover:, and dark: cover nearly everything a dotted map needs. The one habit worth building early is watching for dynamically constructed class names before they cost you a silent styling bug in production, since that's the single issue that won't show up until after a deploy.