Setting the file. One moment.
Skill 16 · Mapbox Style Patterns
Subchapter 16.9
AGENTS.mdMarkdown7 KBView on GitHub
Quick reference for common style patterns, layer configurations, and data-driven styling.
| Layer Type | Use For | Key Properties |
|---|---|---|
| fill | Polygons (countries, parks) | fill-color, fill-opacity |
| line | Roads, boundaries | line-color, line-width |
| symbol | Labels, icons | text-field, icon-image |
| circle | Points (markers, heatmap) | circle-radius, circle-color |
| heatmap | Density visualization | heatmap-intensity, heatmap-color |
| fill-extrusion | 3D buildings | fill-extrusion-height |
| raster | Satellite, aerial imagery | raster-opacity |
// ✅ Color by category
'fill-color': [
'match',
['get', 'type'],
'park', '#90EE90',
'water', '#87CEEB',
'urban', '#D3D3D3',
'#CCCCCC' // default
]
// ✅ Size by numeric value
'circle-radius': [
'interpolate', ['linear'],
['get', 'population'],
0, 5,
1000000, 20
]// ✅ Show/hide by zoom
'visibility': [
'step',
['zoom'],
'none', // Hidden below zoom 10
10, 'visible'
]
// ✅ Size by zoom
'text-size': [
'interpolate', ['linear'],
['zoom'],
8, 10, // Small at zoom 8
16, 18 // Large at zoom 16
]map.addSource('points', {
type: 'geojson',
data: geojson,
cluster: true,
clusterRadius: 50,
clusterMaxZoom: 14
});
// Cluster circles
map.addLayer({
id: 'clusters',
type: 'circle',
source: 'points',
filter: ['has', 'point_count'],
paint: {
'circle-color': ['step', ['get', 'point_count'], '#51bbd6', 100, '#f1f075', 750, '#f28cb1'],
'circle-radius': ['step', ['get', 'point_count'], 20, 100, 30, 750, 40]
}
});// ✅ Hover effect without modifying data
map.on('mousemove', 'layer', (e) => {
if (hoveredId) {
map.setFeatureState(
{ source: 'source', id: hoveredId },
{ hover: false }
);
}
hoveredId = e.features[0].id;
map.setFeatureState(
{ source: 'source', id: hoveredId },
{ hover: true }
);
});
// Style based on state
'fill-color': [
'case',
['boolean', ['feature-state', 'hover'], false],
'#0080ff', // Hover color
'#3bb2d0' // Default color
]For binding the event with the newer addInteraction API instead of map.on(), and for appearances (a declarative alternative to the case/feature-state expression above, symbol layers only), see references/interactions.md.
// ✅ Filter by property
map.setFilter('layer', ['==', ['get', 'type'], 'restaurant']);
// ✅ Filter by multiple conditions
map.setFilter('layer', ['all', ['==', ['get', 'type'], 'restaurant'], ['>', ['get', 'rating'], 4]]);
// ✅ Filter by zoom
map.setFilter('layer', ['all', ['>=', ['zoom'], 10], ['<', ['zoom'], 14]]);// ✅ Conditional styling
'circle-color': [
'case',
['<', ['get', 'value'], 10], '#00ff00', // Green if < 10
['<', ['get', 'value'], 20], '#ffff00', // Yellow if < 20
'#ff0000' // Red otherwise
]
// ✅ Math operations
'circle-radius': [
'*',
['sqrt', ['get', 'population']],
0.01
]
// ✅ String concatenation
'text-field': ['concat', 'Population: ', ['get', 'pop']]Use vector tiles when:
Use GeoJSON when:
// ✅ Set minzoom/maxzoom
map.addLayer({
id: 'layer',
minzoom: 10, // Only show zoom 10+
maxzoom: 16 // Hide above zoom 16
});
// ✅ Use feature state instead of removing/re-adding
// Bad: map.removeLayer() / map.addLayer()
// Good: map.setFeatureState()
// ✅ Combine similar layers
// Bad: Separate layer for each category
// Good: One layer with data-driven styling// ✅ Update paint property
map.setPaintProperty('layer', 'fill-color', '#ff0000');
// ✅ Update layout property
map.setLayoutProperty('layer', 'visibility', 'none');
// ✅ Update source data
map.getSource('source').setData(newGeojson);
// ✅ Batch updates (better performance)
map.once('idle', () => {
// Multiple style changes here
});// ✅ Insert layer at specific position
map.addLayer(
{
id: 'new-layer',
type: 'fill',
source: 'source'
},
'existing-layer-id'
); // Insert before this layermap.addLayer({
id: 'choropleth',
type: 'fill',
source: 'counties',
paint: {
'fill-color': ['interpolate', ['linear'], ['get', 'density'], 0, '#f7fbff', 100, '#08519c'],
'fill-opacity': 0.7
}
});map.addLayer({
id: 'route',
type: 'line',
source: 'route',
paint: {
'line-color': '#0080ff',
'line-width': 5,
'line-opacity': 0.8
}
});map.addLayer({
id: 'buildings',
type: 'fill-extrusion',
source: 'composite',
'source-layer': 'building',
paint: {
'fill-extrusion-color': '#aaa',
'fill-extrusion-height': ['get', 'height'],
'fill-extrusion-base': ['get', 'min_height'],
'fill-extrusion-opacity': 0.8
}
});| Type | Function | Example |
|---|---|---|
| Decision | match, case | Color by category |
| Ramp | interpolate, step | Size by value |
| Math | +, -, *, /, % | Calculate values |
| String | concat, upcase, downcase | Format labels |
| Lookup | get, has, at | Access properties |
| Zoom | zoom | Zoom-based styling |
// ✅ Check if layer exists
if (map.getLayer('layer-id')) {
map.removeLayer('layer-id');
}
// ✅ Check if source exists
if (map.getSource('source-id')) {
map.removeSource('source-id');
}
// ✅ List all layers
console.log(map.getStyle().layers);
// ✅ Get layer paint properties
console.log(map.getPaintProperty('layer', 'fill-color'));