Map
Map Canvas
The map canvas takes up the full viewport. For demo purposes, the container is constrained.
<div
id="map"
style="
width: 100%;
height: 400px;
background: var(--ss-color-surface-muted);
position: relative;
"
>
<div
class="ss-c-map_canvas"
style="width: 100%; height: 100%; position: relative"
>
<!-- Map tiles would be rendered here by a mapping library -->
<div
style="
display: flex;
justify-content: center;
align-items: center;
height: 100%;
color: var(--ss-color-text-muted);
"
>
Map Canvas Placeholder
</div>
</div>
</div>
Layer Controls
Base Layers
<!-- Layer controls fill their positioned map container -->
<div style="position: relative; min-height: 200px">
<div
class="ss-c-layer_control_base"
role="group"
aria-labelledby="map-base-layers"
style="
padding: 16px;
background: var(--ss-color-surface-alt);
color: var(--ss-color-text);
border-radius: var(--ss-radius-lg);
pointer-events: auto;
"
>
<h3 id="map-base-layers">Base Layers</h3>
<label>
<span>
<input
type="radio"
name="base-layer"
value="streets"
checked
/>
<span>Streets</span>
</span>
</label>
<label>
<span>
<input type="radio" name="base-layer" value="satellite" />
<span>Satellite</span>
</span>
</label>
<label>
<span>
<input type="radio" name="base-layer" value="terrain" />
<span>Terrain</span>
</span>
</label>
</div>
</div>
Overlay Controls
<!-- Layer controls fill their positioned map container -->
<div style="position: relative; min-height: 200px">
<div
class="ss-c-layer_control_overlay"
role="group"
aria-labelledby="map-overlays"
style="
padding: 16px;
background: var(--ss-color-surface-alt);
color: var(--ss-color-text);
border-radius: var(--ss-radius-lg);
pointer-events: auto;
"
>
<h3 id="map-overlays">Overlays</h3>
<label>
<span>
<input type="checkbox" name="overlay" value="traffic" />
<span>Traffic</span>
</span>
</label>
<label>
<span>
<input type="checkbox" name="overlay" value="transit" />
<span>Transit</span>
</span>
</label>
<label>
<span>
<input type="checkbox" name="overlay" value="bike" />
<span>Bike Lanes</span>
</span>
</label>
</div>
</div>
Leaflet controls
Inside ss-c-map, Leaflet's zoom bar, layer switcher,
attribution, scale, popups and tooltips read the colour, radius, shadow
and focus tokens, so they follow the dark theme. Leaflet still positions
and sizes them. Load stylescape after leaflet.css. The markup
below is what Leaflet 1.9 renders (no tiles in this demo).
<div class="ss-c-map">
<div class="ss-c-map__canvas leaflet-container leaflet-touch" tabindex="0" aria-label="Harbour map (Leaflet)">
<div class="leaflet-pane leaflet-map-pane">
<div class="leaflet-pane leaflet-tooltip-pane">
<div class="leaflet-tooltip leaflet-tooltip-top" role="tooltip" style="left: 120px; top: 210px;">Pier 4</div>
</div>
<div class="leaflet-pane leaflet-popup-pane">
<div class="leaflet-popup" style="left: 330px; bottom: -150px;">
<div class="leaflet-popup-content-wrapper">
<div class="leaflet-popup-content" style="width: 168px;"><strong>Harbour office</strong><br />Open 8:00–17:00</div>
</div>
<div class="leaflet-popup-tip-container"><div class="leaflet-popup-tip"></div></div>
<a class="leaflet-popup-close-button" role="button" aria-label="Close popup" href="#close"><span aria-hidden="true">×</span></a>
</div>
</div>
</div>
<div class="leaflet-control-container">
<div class="leaflet-top leaflet-left">
<div class="leaflet-control-zoom leaflet-bar leaflet-control">
<a class="leaflet-control-zoom-in" href="#" title="Zoom in" role="button" aria-label="Zoom in"><span aria-hidden="true">+</span></a>
<a class="leaflet-control-zoom-out leaflet-disabled" href="#" title="Zoom out" role="button" aria-label="Zoom out" aria-disabled="true"><span aria-hidden="true">−</span></a>
</div>
</div>
<div class="leaflet-top leaflet-right">
<div class="leaflet-control-layers leaflet-control-layers-expanded leaflet-control">
<section class="leaflet-control-layers-list" aria-label="Layers">
<div class="leaflet-control-layers-base">
<label><span><input type="radio" class="leaflet-control-layers-selector" name="leaflet-base" checked /><span> Streets</span></span></label>
<label><span><input type="radio" class="leaflet-control-layers-selector" name="leaflet-base" /><span> Aerial</span></span></label>
</div>
<div class="leaflet-control-layers-separator"></div>
<div class="leaflet-control-layers-overlays">
<label><span><input type="checkbox" class="leaflet-control-layers-selector" checked /><span> Moorings</span></span></label>
</div>
</section>
</div>
</div>
<div class="leaflet-bottom leaflet-left">
<div class="leaflet-control-scale leaflet-control"><div class="leaflet-control-scale-line" style="width: 82px;">200 m</div></div>
</div>
<div class="leaflet-bottom leaflet-right">
<div class="leaflet-control-attribution leaflet-control"><a href="https://leafletjs.com">Leaflet</a> | © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors</div>
</div>
</div>
</div>
</div>
MapLibre and Mapbox GL controls
The same for .maplibregl-ctrl* and .mapboxgl-ctrl*:
control groups, attribution, scale and popups (every anchor). The
libraries draw their control icons as dark images; in dark mode they
are inverted through --ss-map-icon-filter.
Next departure 14:20
<div class="ss-c-map">
<div class="ss-c-map__canvas maplibregl-map" tabindex="0" aria-label="Harbour map (MapLibre)">
<div class="maplibregl-popup maplibregl-popup-anchor-bottom" style="transform: translate(-50%, -100%) translate(380px, 190px);">
<div class="maplibregl-popup-tip"></div>
<div class="maplibregl-popup-content">
<button class="maplibregl-popup-close-button" type="button" aria-label="Close popup">×</button>
<strong>Ferry terminal</strong><br />Next departure 14:20
</div>
</div>
<div class="maplibregl-control-container">
<div class="maplibregl-ctrl-top-right">
<div class="maplibregl-ctrl maplibregl-ctrl-group">
<button class="maplibregl-ctrl-zoom-in" type="button" title="Zoom in" aria-label="Zoom in"><span class="maplibregl-ctrl-icon" aria-hidden="true"></span></button>
<button class="maplibregl-ctrl-zoom-out" type="button" title="Zoom out" aria-label="Zoom out"><span class="maplibregl-ctrl-icon" aria-hidden="true"></span></button>
<button class="maplibregl-ctrl-compass" type="button" title="Reset bearing to north" aria-label="Reset bearing to north"><span class="maplibregl-ctrl-icon" aria-hidden="true"></span></button>
</div>
</div>
<div class="maplibregl-ctrl-bottom-left">
<div class="maplibregl-ctrl maplibregl-ctrl-scale" style="width: 74px;">100 m</div>
</div>
<div class="maplibregl-ctrl-bottom-right">
<div class="maplibregl-ctrl maplibregl-ctrl-attrib">
<div class="maplibregl-ctrl-attrib-inner"><a href="https://maplibre.org/">MapLibre</a> | © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a></div>
</div>
</div>
</div>
</div>
</div>
Other map libraries
ss-c-map themes the chrome of Leaflet, MapLibre GL and
Mapbox GL only. OpenLayers, Google Maps and ArcGIS draw their own
controls and keep their own look; they are not themed on purpose,
because each ships its own stylesheet and class structure. The frame, canvas and ss-c-map__control overlay work
with any library. To theme another library's controls, scope a few
rules under .ss-c-map with the same tokens, for
example for OpenLayers:
.ss-c-map .ol-control button {
background-color: var(--ss-color-surface);
color: var(--ss-color-text);
border-radius: 0;
}
.ss-c-map .ol-control button:hover {
background-color: var(--ss-color-surface-hover);
}
.ss-c-map .ol-attribution {
background-color: var(--ss-color-surface);
color: var(--ss-color-text);
}