Prism
← All primitives

NeonMap

Map

A real interactive vector map (pan/zoom/tilt) recolored into a neon nightscape, with three tunable looks — Streets, Geography, Attractions — plus a glowing arc and pulsing pin. Tiles + style from OpenFreeMap — genuinely free, no API token.

Requires the .neon-pin / neon-pulse styles from globals.css and the maplibre-gl CSS (imported in the component). Give it a sized container; render via next/dynamic ssr:false. Separate WebGL context from R3F — don't stack with heavy 3D scenes.

$npx shadcn@latest add https://prism.icglabs.co/r/neon-map.json
Dependencies:maplibre-gl
View raw manifest →

components/prism/NeonMap.tsx
"use client";

import { useEffect, useRef, useState } from "react";
import maplibregl from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css";

/**
 * NeonMap — a REAL interactive vector map (actual streets, pan/zoom/tilt) styled
 * into a neon nightscape, with three tunable looks: Streets (glowing roads),
 * Geography (roads off, glowing water + land), and Attractions (dim roads, bright
 * points of interest). Tiles + style from OpenFreeMap (free, no API token). The
 * `.neon-pin` styles live in globals.css. Separate WebGL context from R3F.
 */
const NEON = "#1fd4e6";
const MAGENTA = "#ff3d81";

type Mode = "streets" | "geography" | "attractions";
const MODES: { id: Mode; label: string }[] = [
  { id: "streets", label: "Streets" },
  { id: "geography", label: "Geography" },
  { id: "attractions", label: "Attractions" },
];

function archedLine(a: [number, number], b: [number, number], n = 48, lift = 9) {
  const pts: [number, number][] = [];
  for (let i = 0; i <= n; i++) {
    const t = i / n;
    pts.push([a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t + Math.sin(Math.PI * t) * lift]);
  }
  return pts;
}

function applyMode(map: maplibregl.Map, mode: Mode) {
  const layers = map.getStyle().layers ?? [];
  for (const layer of layers) {
    const id = layer.id;
    const type = layer.type;
    const isRoad = /(road|street|highway|transportation|bridge|tunnel|rail)/.test(id);
    const isWater = /water|ocean|river|lake/.test(id);
    const isLand = /(landcover|landuse|park|forest|wood|grass|nature|sand|glacier)/.test(id);
    const isBuilding = /building/.test(id);
    const isPoi = /poi/.test(id);
    const isLabel = type === "symbol";

    const show = (v: boolean) => {
      try {
        map.setLayoutProperty(id, "visibility", v ? "visible" : "none");
      } catch {}
    };
    const paint = (prop: string, val: unknown) => {
      try {
        map.setPaintProperty(id, prop, val);
      } catch {}
    };

    if (mode === "streets") {
      if (isRoad && type === "line") {
        show(true);
        paint("line-color", NEON);
        paint("line-blur", 0.6);
      }
      if (isWater && type === "fill") {
        show(true);
        paint("fill-color", "#081018");
      }
      if (isLand && type === "fill") {
        show(true);
        paint("fill-color", "#0a0f18");
      }
      if (isBuilding) {
        show(true);
        paint(type === "fill-extrusion" ? "fill-extrusion-color" : "fill-color", "#0c1320");
      }
      if (isPoi) show(false);
      if (isLabel) show(true);
    } else if (mode === "geography") {
      if (isRoad) show(false); // less streets
      if (isWater && type === "fill") {
        show(true);
        paint("fill-color", "#06263a"); // glowing water
      }
      if (isLand && type === "fill") {
        show(true);
        paint("fill-color", "#0f3a2e"); // glowing land / parks
      }
      if (isBuilding) show(false);
      if (isPoi) show(false);
      if (isLabel && !/place|country|state|continent/.test(id)) show(false); // keep only big place names
    } else {
      // attractions
      if (isRoad && type === "line") {
        show(true);
        paint("line-color", "#163240"); // dim roads
        paint("line-blur", 0.3);
      }
      if (isWater && type === "fill") {
        show(true);
        paint("fill-color", "#081018");
      }
      if (isLand && type === "fill") {
        show(true);
        paint("fill-color", "#0a0f18");
      }
      if (isBuilding) {
        show(true);
        paint(type === "fill-extrusion" ? "fill-extrusion-color" : "fill-color", "#0f1726");
      }
      if (isPoi) {
        show(true);
        if (type === "symbol") paint("text-color", "#ffb24d");
        if (type === "circle") paint("circle-color", "#ffb24d");
      }
      if (isLabel) show(true);
    }
  }
}

export function NeonMap({
  center = [-122.42, 37.77],
  zoom = 12,
}: {
  center?: [number, number];
  zoom?: number;
}) {
  const ref = useRef<HTMLDivElement>(null);
  const mapRef = useRef<maplibregl.Map | null>(null);
  const loadedRef = useRef(false);
  const [mode, setMode] = useState<Mode>("streets");
  const modeRef = useRef<Mode>(mode);
  modeRef.current = mode;

  useEffect(() => {
    if (!ref.current || mapRef.current) return;
    const map = new maplibregl.Map({
      container: ref.current,
      style: "https://tiles.openfreemap.org/styles/dark",
      center,
      zoom,
      pitch: 55,
      bearing: -18,
      attributionControl: { compact: true },
    });
    mapRef.current = map;

    map.on("load", () => {
      try {
        map.setPaintProperty("background", "background-color", "#05060a");
      } catch {}

      const arc: GeoJSON.Feature = {
        type: "Feature",
        properties: {},
        geometry: { type: "LineString", coordinates: archedLine(center, [center[0] + 0.06, center[1] + 0.05]) },
      };
      map.addSource("arc", { type: "geojson", data: arc });
      map.addLayer({
        id: "arc-glow",
        type: "line",
        source: "arc",
        paint: { "line-color": MAGENTA, "line-width": 4, "line-blur": 6, "line-opacity": 0.9 },
      });
      map.addLayer({
        id: "arc-core",
        type: "line",
        source: "arc",
        paint: { "line-color": "#ffffff", "line-width": 1.2 },
      });

      const el = document.createElement("div");
      el.className = "neon-pin";
      new maplibregl.Marker({ element: el }).setLngLat(center).addTo(map);

      loadedRef.current = true;
      applyMode(map, modeRef.current);
    });

    const ro = new ResizeObserver(() => map.resize());
    ro.observe(ref.current);

    return () => {
      ro.disconnect();
      map.remove();
      mapRef.current = null;
      loadedRef.current = false;
    };
  }, [center, zoom]);

  useEffect(() => {
    const map = mapRef.current;
    if (map && loadedRef.current) applyMode(map, mode);
  }, [mode]);

  return (
    // data-lenis-prevent: stop the page's Lenis smooth-scroll from also scrolling
    // when you wheel/zoom over the map.
    <div className="relative h-full w-full" data-lenis-prevent>
      <div ref={ref} className="h-full w-full" />
      <div className="absolute left-4 top-4 z-10 flex gap-2">
        {MODES.map((m) => (
          <button
            key={m.id}
            onClick={() => setMode(m.id)}
            className={`rounded-full border px-3 py-1.5 text-xs backdrop-blur transition-colors ${
              mode === m.id
                ? "border-cyan/50 bg-cyan/10 text-ink"
                : "border-line bg-obsidian/50 text-mist hover:text-ink"
            }`}
          >
            {m.label}
          </button>
        ))}
      </div>
    </div>
  );
}
Live demo — read-only. Every section is a real, copyable primitive.