> For the complete documentation index, see [llms.txt](https://codifi-fdm.gitbook.io/codifi-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://codifi-fdm.gitbook.io/codifi-docs/web-app/map-setup-and-offline-areas/map-overlays/styles/data-driven-styling.md).

# Data-Driven Styling

A **data-driven style** lets a layer's appearance — color, size, opacity, visibility, anything — change based on each feature's attribute values, on the current zoom level, or on logical conditions you build. Instead of "every site polygon is blue," you can say "every Site polygon is colored by its `Site Type` attribute, fading to white above zoom 18, and only shown when `Status = Active`."

The Map Style Editor exposes data-driven styling through four visual builders, plus a raw JSON fallback for anything they don't cover.

***

## When to Reach for Data-Driven Styling

Use a data-driven expression whenever a property's value depends on something *about the feature* or *about the map*. Common cases:

* **Color by attribute** — sites colored by Resource Type, transects by Survey Year, parcels by zoning code.
* **Size by numeric value** — circle radius scaled to artifact count, line width to road class.
* **Show / hide by zoom** — heatmap visible at city scale, individual markers visible at survey scale.
* **Filter by status** — only render features where `Status = "Complete"` or `Confidence > 0.8`.
* **Combine multiple conditions** — color by type, but if no type is set, fall back to a default color; or show one icon for points inside a Project area, another for points outside.

If a property's value is the same for every feature regardless of attributes or zoom, leave it as a flat literal value — there's no benefit to wrapping it in an expression.

***

## The Four Visual Builders

Click the **expression button** (an icon next to a property's editor) to open the builder picker. The Map Style Editor provides four visual builders that handle the most common patterns:

### Match — pick a value from a list

Use **Match** when you have a categorical attribute (a string or enum) and you want to map specific values to specific outputs.

> **Example: color sites by their archetype.**

| Input field | Match value         | Output color       |
| ----------- | ------------------- | ------------------ |
| `archetype` | `"Site"`            | `#E63946` (red)    |
| `archetype` | `"Feature"`         | `#F1C40F` (yellow) |
| `archetype` | `"Isolated Find"`   | `#3498DB` (blue)   |
| *fallback*  | (none of the above) | `#7F8C8D` (gray)   |

The Match builder lets you add rows for each known value, plus a fallback for anything not listed. It generates a Mapbox `["match", ...]` expression under the hood.

### Case — sequence of conditions

Use **Case** when the input isn't a single field but a more complex condition (a comparison, a range check, a combination of fields).

> **Example: highlight features that need review.**

| Condition                                   | Output color       |
| ------------------------------------------- | ------------------ |
| `Status = "Pending"` AND `Confidence < 0.5` | `#E74C3C` (red)    |
| `Status = "Pending"`                        | `#F39C12` (orange) |
| *otherwise*                                 | `#27AE60` (green)  |

The Case builder evaluates conditions top-to-bottom and uses the first one that matches. Conditions are built with the **Visual Condition Builder** — a row-by-row UI for AND/OR groupings, comparisons, and field references.

### Interpolate — smooth blend between numeric stops

Use **Interpolate** when you want a property to vary smoothly as a numeric input changes — typically a numeric attribute on the feature, or the current zoom level.

> **Example: circle radius scales with artifact count.**

| Artifact Count | Circle Radius |
| -------------- | ------------- |
| 0              | 4 px          |
| 10             | 8 px          |
| 50             | 14 px         |
| 200            | 24 px         |

Between stops, the editor blends linearly (or via an exponential / cubic-bezier easing). For values outside the listed range, the editor clamps to the nearest endpoint.

### Filter — exclude features from a layer

A **Filter** isn't a property value — it's a condition that determines *whether the feature is rendered at all* by the current layer. Apply a filter to a layer to scope it to a subset of features.

> **Example: a layer that only renders Sites with a recorded year.**
>
> Filter: `archetype = "Site"` AND `recordedYear is not empty`

Filters use the same Visual Condition Builder as Case expressions. They're additive — apply one filter to one layer, a different filter to another, and stack the layers to render different cuts of the same overlay differently.

***

## Zoom and Interpolate Expressions

Zoom-based interpolation is a special case of Interpolate where the input is the map's current zoom level. The Map Style Editor exposes this as a dedicated **Zoom Property** editor (a quick way to set zoom-stops on any numeric property like `radius`, `width`, or `opacity`).

> **Example: line width grows with zoom.**

| Zoom | Line Width |
| ---- | ---------- |
| 8    | 1 px       |
| 14   | 2 px       |
| 18   | 4 px       |

This pattern keeps small-scale zoom views uncluttered and large-scale zoom views readable, without you having to pick a single width that compromises both.

***

## Available Fields

Data-driven expressions can reference any of the **available fields** on the source layer:

* **For map overlays**, available fields are the GeoJSON properties on each feature — exactly what you'd see in the overlay's Data Table popup.
* **For archetypes** (when you're styling an archetype's Records in the Library), available fields are pulled automatically from the archetype's section container — every Field on the archetype is exposed by its label.

The Match, Case, and Interpolate builders show a dropdown of available fields rather than making you type field names. This eliminates a common source of bugs (a typo in a field reference makes the whole expression silently fail).

***

## Combining Multiple Layers vs. One Complex Expression

There's almost always more than one way to achieve a given visual outcome. As a rule of thumb:

* **Use multiple layers** when the visual outcomes are conceptually distinct (a heatmap layer + a marker layer), when you want them to coexist (the heatmap shows context behind the markers), or when each needs its own filter.
* **Use one layer with a complex expression** when the property value is the only thing that varies (color, radius, opacity), and every feature is rendered the same *kind* of way.

Multiple layers are easier to debug and modify later. Complex expressions are tighter and don't add to the layer list. Pick whichever a future maintainer will find more readable.

***

## Falling Back to Raw Expressions

For expression types the visual builders don't cover (mathematical operators, string functions, geometry tests like `within`), each property has a **Raw JSON** option in the expression picker. The raw editor accepts any valid Mapbox style expression. See [JSON Styling & the Mapbox Spec](/codifi-docs/web-app/map-setup-and-offline-areas/map-overlays/styles/json-styling.md) for the full expression reference.

If you're using the visual builders and find yourself wishing for a feature they don't expose, the raw fallback is always available — and any expression you author there will round-trip cleanly back into the visual builder if it matches one of the known patterns the next time you open it.

***

## See Also

* [Layers & Properties](/codifi-docs/web-app/map-setup-and-offline-areas/map-overlays/styles/layers-and-properties.md) — what each property type does on each layer
* [JSON Styling & the Mapbox Spec](/codifi-docs/web-app/map-setup-and-offline-areas/map-overlays/styles/json-styling.md) — direct JSON editing and the underlying Mapbox style spec
