All skills
mapbox avatar

/mapbox-style-patterns

@9077a82 official
by mapboxmapbox/mapbox-agent-skills80 stars
17

Common style patterns, layer configurations, and recipes for typical mapping scenarios including restaurant finders, real estate, data visualization, navigation, delivery/logistics, and more. Use when implementing specific map use cases or looking for proven style patterns.

Use this Skill: https://skilld.dev/gh/mapbox/mapbox-agent-skills/mapbox-style-patterns

This session only. Nothing lands on disk.

AGENTS.md

≈1.7k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Mapbox Style Patterns

Quick reference for common style patterns, layer configurations, and data-driven styling.

Layer Types Quick Reference

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

Data-Driven Styling Patterns

Based on Property Value

// ✅ 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
]

Based on Zoom Level

// ✅ 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
]

Common Patterns

1. Clustering

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]
  }
});

2. Feature State (Hover/Selection)

// ✅ 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.

3. Filters

// ✅ 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]]);

4. Expressions

// ✅ 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']]

Performance Patterns

Vector Tiles vs GeoJSON

Use vector tiles when:

  • Large datasets (>5MB)
  • Need different zoom levels
  • Want server-side updates

Use GeoJSON when:

  • Small datasets (<5MB)
  • Frequent client-side updates
  • Simple implementation needed

Layer Optimization

// ✅ 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

Style Management

Dynamic Style Updates

// ✅ 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
});

Before Layers

// ✅ Insert layer at specific position
map.addLayer(
  {
    id: 'new-layer',
    type: 'fill',
    source: 'source'
  },
  'existing-layer-id'
); // Insert before this layer

Common Use Cases

Choropleth Map

map.addLayer({
  id: 'choropleth',
  type: 'fill',
  source: 'counties',
  paint: {
    'fill-color': ['interpolate', ['linear'], ['get', 'density'], 0, '#f7fbff', 100, '#08519c'],
    'fill-opacity': 0.7
  }
});

Route Visualization

map.addLayer({
  id: 'route',
  type: 'line',
  source: 'route',
  paint: {
    'line-color': '#0080ff',
    'line-width': 5,
    'line-opacity': 0.8
  }
});

3D Buildings

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
  }
});

Quick Reference: Expression Types

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

Debugging Tips

// ✅ 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'));

Source: SKILL.md on GitHub

No alerts14d5 checks · Risk SAFE
  • Gen Agent Trust Hub14d

    This skill provides Mapbox map style patterns and configuration recipes. No security vulnerabilities, malicious code, or suspicious behaviors were detected. All external resource references are standard Mapbox vendor services.

  • Socket14d

    No alerts

  • Snyk14d

    Risk: LOW · No issues

  • Runlayer6mo

    1/2 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 9077a82. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 9 hours ago.

Activeupdated 2 weeks ago
  • mapbox
  • cartography
  • style-patterns
  • layer-configuration
  • poi-finder
  • real-estate
  • data-visualization
  • navigation
  • delivery-logistics

README badge

README badge for mapbox/mapbox-agent-skills/mapbox-style-patterns

Provides battle-tested Mapbox style layer configurations and recipes for common mapping scenarios including restaurant finders, real estate, data visualization, navigation, and delivery tracking. Use this when building a new map style or optimizing layer configurations for a specific use case like POI discovery or logistics tracking.

Generated from the current SKILL.md.

Does this skill cover dark mode styles?
Yes. The skill includes a Dark Mode / Night Theme pattern in references/dark-mode.md with near-black backgrounds and reduced brightness suitable for low-light environments.
Can I use these patterns for real estate maps?
Yes. The skill provides a Real Estate pattern in references/real-estate.md that handles property boundaries, price color-coding, and amenity markers.
Does this include delivery/logistics tracking?
Yes. The Delivery/Logistics pattern in references/delivery-logistics.md covers real-time tracking, zones, driver markers, and ETA badges, including pulse animations for customer markers.
How do I optimize performance for dense data?
The skill provides a Simplified by Zoom pattern that filters layers based on zoom level, and references/expressions-clustering.md covers data-driven expressions and clustering for dense POIs.
Are these patterns tested for mobile and desktop?
The skill includes a visual regression checklist covering zoom levels, mobile (375px), desktop (1920px), dense/sparse data, label collision, and color contrast, but you must run these tests yourself.

Generated from the current SKILL.md. These answers refresh after source changes.