`{line,fill,circle,..}-layer-opacity` for feature-uniform opacity
# Design Proposal: `{line,fill,circle,..}-layer-opacity` for feature-uniform opacity
## Motivation
Map styles **today** expose opacity primarily as a **feature-level** rendering property, where blending is applied during rasterization and compositing of individual geometries within a layer.
This ties visual intensity to feature-by-feature accumulation rather than treating a layer as a single visual unit.
In practice, style authors often **need** to control the **opacity of entire layers as cohesive entities**, for example to:
- fade thematic layers in or out based on zoom
- smoothly transition visibility during storytelling or temporal animation
- adjust emphasis without changing intra-layer feature contrast
- hiding data-overlaps on transparent fill layers
These patterns are currently approximated using workarounds such as layer duplication or feature-level opacity tuning, which do not cleanly separate feature styling from layer-level presentation control.
This proposal introduces a missing abstraction: uniform post-compositing control over a layer’s rendered output.
Currently, having opacity on lines cannot be done since every feature is rendered by itself.
It looks buggy.
<img width="400px" src="https://github.com/user-attachments/assets/3df888b0-6e72-4091-ab6c-40dfdea7c4e9" />
## Proposed Change
This proposal introduces a clear separation between:
- feature-level styling (how individual geometries are rendered and blended within a layer), and
- layer-level compositing (how the fully rendered layer is integrated into the map)
`{filll,circle,heatmap,line,symbol,hillshade,color-relief}-layer-opacity` applies a uniform alpha multiplier to the fully composited output of a layer, after feature rendering and before final integration into the framebuffer.
This enables treating a layer as a single visual unit, while preserving existing feature-level opacity semantics. The property is orthogonal to existing *-opacity values and supports transitions and Zoom-based expressions.
So as a precise spec:
> This property applies a **uniform alpha multiplier** to the **fully composited raster result of a single layer**, **after all feature-level opacity blending** and **before integration into the framebuffer**.
It does **not affect in- or inter feature oppactity accumulation**.
<details><summary>Or visually (click to expand)</summary>
<p>


Or in more concrete usecases
| using `line-opacity` vs `line-layer-opacity` |
|--|
| <img width="1406" height="753" alt="image" src="https://github.com/user-attachments/assets/6d45c2fe-c0f3-4610-bbf8-a76c06fd7bec" /> |
| `line-opacity` | `line-layer-opacity` |
|--------|--------|
| <img width="100%" alt="image" src="https://github.com/user-attachments/assets/8540a1c0-e81d-4bb8-8d62-03a9cb08b12d" /> | <img width="100%" alt="image" src="https://github.com/user-attachments/assets/4db9e197-076b-43c1-b96d-51e4f80792c9" /> |
See https://github.com/maplibre/maplibre/discussions/503 for more diagrams
</p>
</details>
## API Modifications
Add `{line,fill,circle,..}-layer-opacity`.
These properties:
- accept values in the range 0..=1
- default to 1
- are transitionable
- support zoom expressions
- are data-constant (because they interact with an entire layer, data-driven or feature-state would not make sense)
## Migration Plan and Compatibility
This is backwards compatible.
## Rejected Alternatives
Some alternatives and workarounds discussed include:
- continuing to use existing `*-opacity` properties,
- rendering layers into intermediate frame buffers manually,
- duplicating layers with adjusted negative styling,
- or introducing special blending/.. behaviour into existing opacity properties.
However, these approaches are either:
- visually inconsistent,
- inefficient,
- difficult to author,
- or unable to reproduce true feature-uniform opacity behavior cleanly.
Additional discussion and alternative approaches:
https://github.com/maplibre/maplibre/discussions/503.
Furthermore, we rejected a "bare" `layer-opacity` because while consistent with `resampling` and `visibility`, it is inconsistent with the rest of the `line-*` properties, so adding the prefix is maybe better.
## Implementation roadmap
I have PRs are open for:
- [x] line https://github.com/maplibre/maplibre-style-spec/pull/1650 https://github.com/maplibre/maplibre-gl-js/pull/7570
- [x] fill https://github.com/maplibre/maplibre-style-spec/pull/1650 https://github.com/maplibre/maplibre-gl-js/pull/7570
- [ ] symbol
- [ ] circle
- [ ] heatmap
- [ ] fill-extrusion
- [ ] hillshade
- [ ] color-relief
- raster - wontfix because semantically equivalent existing properties exist
- background - wontfix because semantically equivalent existing properties exist
The implementation plan for all layer types is the same as noted above
> This property applies a **uniform alpha multiplier** to the **fully composited raster result of a single layer**, **after all feature-level opacity blending** and **before integration into the framebuffer**.
It does **not affect in- or inter feature opacity accumulation**.
26 条评论