diff --git a/assets/javascripts/news.json b/assets/javascripts/news.json
index 6753637e29..38bd3ed1d6 100644
--- a/assets/javascripts/news.json
+++ b/assets/javascripts/news.json
@@ -1,4 +1,8 @@
[
+ [
+ "2026-07-09",
+ "New documentation: MapLibre GL JS"
+ ],
[
"2026-07-08",
"New documentation: Odin"
diff --git a/assets/stylesheets/application.css.scss b/assets/stylesheets/application.css.scss
index 7ea6d0fe48..5dd9f6c99b 100644
--- a/assets/stylesheets/application.css.scss
+++ b/assets/stylesheets/application.css.scss
@@ -76,6 +76,7 @@
@use 'pages/love';
@use 'pages/lua';
@use 'pages/gnu_make';
+@use 'pages/maplibre_gl';
@use 'pages/mariadb';
@use 'pages/mdn';
@use 'pages/meteor';
diff --git a/assets/stylesheets/pages/_maplibre_gl.scss b/assets/stylesheets/pages/_maplibre_gl.scss
new file mode 100644
index 0000000000..513943e9f1
--- /dev/null
+++ b/assets/stylesheets/pages/_maplibre_gl.scss
@@ -0,0 +1,17 @@
+@use 'global/classes';
+
+._maplibre_gl {
+ > h1 { @extend %block-heading; }
+ > h2 { @extend %block-heading; }
+ > h3 { @extend %block-label, %label-blue; }
+
+ code { @extend %label; }
+
+ .admonition { @extend %note; }
+ .admonition.tip { @extend %note-green; }
+ .admonition.note, .admonition.info { @extend %note-blue; }
+ .admonition.warning { @extend %note-orange; }
+ .admonition-title {
+ font-weight: var(--bolderFontWeight);
+ }
+}
diff --git a/lib/docs/filters/maplibre_gl/clean_html.rb b/lib/docs/filters/maplibre_gl/clean_html.rb
new file mode 100644
index 0000000000..126a1f475d
--- /dev/null
+++ b/lib/docs/filters/maplibre_gl/clean_html.rb
@@ -0,0 +1,32 @@
+module Docs
+ class MaplibreGl
+ class CleanHtmlFilter < Filter
+ def call
+ # Anchor icons next to headings.
+ css('.headerlink').remove
+
+ # The TypeDoc pages carry a "Defined in: " paragraph on
+ # every member; it points at a pinned commit and adds noise.
+ css('p').each do |node|
+ node.remove if node.content.start_with?('Defined in:')
+ end
+
+ # MkDocs Material renders highlighted code as
+ #
+ # with per-line anchors. Flatten each block to its plain text and tag
+ # the language so DevDocs can re-highlight it.
+ css('div.highlight').each do |node|
+ language = node['class'][/language-(\w+)/, 1]
+ pre = node.at_css('pre')
+ next unless pre
+
+ pre['data-language'] = language if language
+ pre.content = pre.at_css('code').content
+ node.replace(pre)
+ end
+
+ doc
+ end
+ end
+ end
+end
diff --git a/lib/docs/filters/maplibre_gl/entries.rb b/lib/docs/filters/maplibre_gl/entries.rb
new file mode 100644
index 0000000000..61b36da220
--- /dev/null
+++ b/lib/docs/filters/maplibre_gl/entries.rb
@@ -0,0 +1,52 @@
+module Docs
+ class MaplibreGl
+ class EntriesFilter < Docs::EntriesFilter
+ # Sections of a TypeDoc class page whose members are worth indexing
+ # individually.
+ MEMBER_SECTIONS = %w(Constructors Properties Methods Events Accessors).freeze
+
+ def get_name
+ at_css('h1').content.strip
+ end
+
+ def get_type
+ # DevDocs lower-cases paths, so match against the lower-cased segments.
+ case path
+ when %r{api/classes/} then 'Classes'
+ when %r{api/functions/} then 'Functions'
+ when %r{api/type-aliases/} then 'Type Aliases'
+ when %r{api/interfaces/} then 'Interfaces'
+ when %r{api/enumerations/} then 'Enumerations'
+ when %r{api/variables/} then 'Variables'
+ when %r{\Aguides/} then 'Guides'
+ else 'Miscellaneous'
+ end
+ end
+
+ def additional_entries
+ return [] unless class_page?
+
+ entries = []
+ section = nil
+
+ css('h2, h3').each do |node|
+ if node.name == 'h2'
+ section = node.content.strip
+ elsif MEMBER_SECTIONS.include?(section)
+ member = node.content.strip.sub(/\(\)\z/, '').sub(/\?\z/, '')
+ next if member.empty?
+ entries << ["#{get_name}.#{member}", "#{path}##{node['id']}"]
+ end
+ end
+
+ entries
+ end
+
+ private
+
+ def class_page?
+ path.include?('api/classes/')
+ end
+ end
+ end
+end
diff --git a/lib/docs/scrapers/maplibre_gl.rb b/lib/docs/scrapers/maplibre_gl.rb
new file mode 100644
index 0000000000..a36ca6d3d5
--- /dev/null
+++ b/lib/docs/scrapers/maplibre_gl.rb
@@ -0,0 +1,47 @@
+module Docs
+ class MaplibreGl < UrlScraper
+ self.name = 'MapLibre GL JS'
+ self.slug = 'maplibre_gl'
+ self.type = 'maplibre_gl'
+ self.release = '5.24.0'
+ self.base_url = 'https://maplibre.org/maplibre-gl-js/docs/'
+ self.root_path = '/'
+ self.links = {
+ home: 'https://maplibre.org/maplibre-gl-js/docs/',
+ code: 'https://github.com/maplibre/maplibre-gl-js'
+ }
+
+ html_filters.push 'maplibre_gl/clean_html', 'maplibre_gl/entries'
+
+ options[:container] = '.md-content__inner'
+
+ # Only scrape the TypeDoc-generated API reference and the guides.
+ # The examples (200+ interactive demos) and the embedded style
+ # specification are excluded as they aren't API reference material.
+ options[:only_patterns] = [
+ %r{\AAPI/classes/},
+ %r{\AAPI/functions/},
+ %r{\AAPI/type-aliases/},
+ %r{\AAPI/interfaces/},
+ %r{\AAPI/enumerations/},
+ %r{\AAPI/variables/},
+ %r{\Aguides/}
+ ]
+
+ # The site's navigation links point at the `www.` host, which then
+ # redirects (301) to the bare domain used by `base_url`. Rewrite them so
+ # they are recognised as internal URLs and get followed.
+ options[:fix_urls] = ->(url) do
+ url.sub(%r{\Ahttps://www\.maplibre\.org/}, 'https://maplibre.org/')
+ end
+
+ options[:attribution] = <<-HTML
+ © MapLibre contributors
+ Licensed under the 3-Clause BSD License.
+ HTML
+
+ def get_latest_version(opts)
+ get_npm_version('maplibre-gl', opts)
+ end
+ end
+end
diff --git a/lib/docs/scrapers/mdn/javascript.rb b/lib/docs/scrapers/mdn/javascript.rb
index 87ce6430ed..913da9a418 100644
--- a/lib/docs/scrapers/mdn/javascript.rb
+++ b/lib/docs/scrapers/mdn/javascript.rb
@@ -3,7 +3,7 @@ class Javascript < Mdn
prepend FixInternalUrlsBehavior
prepend FixRedirectionsBehavior
- # release = '2026-05-26'
+ # release = '2026-07-08'
self.name = 'JavaScript'
self.base_url = 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference'
self.links = {
diff --git a/public/icons/docs/maplibre_gl/16.png b/public/icons/docs/maplibre_gl/16.png
new file mode 100644
index 0000000000..00c1db8283
Binary files /dev/null and b/public/icons/docs/maplibre_gl/16.png differ
diff --git a/public/icons/docs/maplibre_gl/16@2x.png b/public/icons/docs/maplibre_gl/16@2x.png
new file mode 100644
index 0000000000..49f4b9e988
Binary files /dev/null and b/public/icons/docs/maplibre_gl/16@2x.png differ
diff --git a/public/icons/docs/maplibre_gl/SOURCE b/public/icons/docs/maplibre_gl/SOURCE
new file mode 100644
index 0000000000..ddd48e6326
--- /dev/null
+++ b/public/icons/docs/maplibre_gl/SOURCE
@@ -0,0 +1 @@
+SOURCE: https://raw.githubusercontent.com/maplibre/maplibre-gl-js/main/docs/assets/logo.svg