All territories are always included — disputed and partially-recognized ones carry a disputed: true property, making it easy to style them independently. This view is centered on Europe and the Middle East, where disputed territories are most dense.
https://api.mapjson.com/v1/geo?layer=countries&properties=name
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/topojson-client@3/dist/topojson-client.min.js"></script>
<style>
svg { width: 100%; display: block; background: #a8c4d8; }
#tooltip {
position: fixed; background: #000; color: #fff;
font-size: 12px; padding: 4px 8px; pointer-events: none; opacity: 0;
}
</style>
</head>
<body>
<div id="map"></div>
<div id="tooltip"></div>
<script>
const svg = d3.select("#map").append("svg").attr("viewBox", "0 0 960 560");
const projection = d3.geoMercator().center([30, 42]).scale(680).translate([480, 280]);
const path = d3.geoPath().projection(projection);
const tooltip = d3.select("#tooltip");
d3.json("https://api.mapjson.com/v1/geo?layer=countries&detail=medium&properties=name").then(topo => {
const features = topojson.feature(topo, topo.objects.geo).features;
svg.append("g")
.selectAll("path")
.data(features.filter(d => !d.properties.disputed))
.join("path").attr("d", path).attr("fill", "#ddd8cc").attr("stroke", "#f0ece6").attr("stroke-width", 0.5);
svg.append("g")
.selectAll("path")
.data(features.filter(d => d.properties.disputed))
.join("path").attr("d", path)
.attr("fill", "#e07840").attr("stroke", "#c05820").attr("stroke-width", 1)
.style("cursor", "pointer")
.on("mouseover", function(event, d) {
d3.select(this).attr("fill", "#c05820");
tooltip.style("opacity", 1).text(d.properties.name || "disputed territory");
})
.on("mousemove", function(event) {
tooltip.style("left", event.clientX + 12 + "px").style("top", event.clientY - 8 + "px");
})
.on("mouseout", function() { d3.select(this).attr("fill", "#e07840"); tooltip.style("opacity", 0); });
});
</script>
</body>
</html>