From 547ef26c1e806dbb28ccf4e44b4eae791f9bb2fa Mon Sep 17 00:00:00 2001 From: adarshsm <24850536+adarshsm@users.noreply.github.com> Date: Fri, 31 Jul 2026 17:08:37 +0530 Subject: [PATCH 1/2] Vendor a glyphicons-only stylesheet instead of the full Bootstrap 3 base folium loaded netdna's bootstrap-glyphicons.css for the Awesome Markers glyphicon font. Despite its name that file is a full Bootstrap 3 base stylesheet: alongside the @font-face/.glyphicon rules it ships a normalize reset and global body styles (font, colour, background, margin). Loaded after Bootstrap 5, those body rules leaked onto the host page and, e.g., mispositioned user-added Bootstrap 5 components (GH-1820). Ship a vendored folium/templates/glyphicons.css with only the @font-face and .glyphicon/.glyphicon-* rules (fonts served from jsDelivr), referenced the same way as leaflet.awesome.rotate.css. Icon(prefix="glyphicon") markers keep working; the page-wide body/reset pollution is gone. Closes #1820 --- folium/folium.py | 7 +- folium/templates/glyphicons.css | 211 ++++++++++++++++++++++++++++++++ tests/test_folium.py | 10 ++ 3 files changed, 226 insertions(+), 2 deletions(-) create mode 100644 folium/templates/glyphicons.css diff --git a/folium/folium.py b/folium/folium.py index 8983ce2721..8ba0dfda9b 100644 --- a/folium/folium.py +++ b/folium/folium.py @@ -43,10 +43,13 @@ "bootstrap_css", "https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css", ), - # glyphicons came from Bootstrap 3 and are used for Awesome Markers + # glyphicons came from Bootstrap 3 and are used for Awesome Markers. + # This vendored file has only the @font-face/.glyphicon rules; the previous + # netdna stylesheet also shipped a full Bootstrap-3 reset that leaked global + # ``body`` styles onto the host page (GH-1820). ( "glyphicons_css", - "https://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-glyphicons.css", + "https://cdn.jsdelivr.net/gh/python-visualization/folium/folium/templates/glyphicons.css", ), ( "awesome_markers_font_css", diff --git a/folium/templates/glyphicons.css b/folium/templates/glyphicons.css new file mode 100644 index 0000000000..f0ac109cda --- /dev/null +++ b/folium/templates/glyphicons.css @@ -0,0 +1,211 @@ +/* + * Glyphicons Halflings -- the icon font used by folium's default Awesome Markers + * (Icon(prefix="glyphicon")). Extracted from Bootstrap 3's glyphicon rules only. + * + * folium previously loaded netdna's bootstrap-glyphicons.css, which despite its + * name ships a full Bootstrap-3 base stylesheet (normalize reset + global `body` + * rules) that leaked onto the host page (GH-1820). This file provides just the + * @font-face and .glyphicon/.glyphicon-* rules needed for the markers, with no + * page-wide side effects. Font files are served from jsDelivr. + */ +@font-face{font-family:'Glyphicons Halflings';src:url('https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/fonts/glyphicons-halflings-regular.eot');src:url('https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),url('https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/fonts/glyphicons-halflings-regular.woff') format('woff'),url('https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/fonts/glyphicons-halflings-regular.ttf') format('truetype'),url('https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/fonts/glyphicons-halflings-regular.svg#glyphicons-halflingsregular') format('svg');}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;} +.glyphicon-asterisk:before{content:"\2a";} +.glyphicon-plus:before{content:"\2b";} +.glyphicon-euro:before{content:"\20ac";} +.glyphicon-minus:before{content:"\2212";} +.glyphicon-cloud:before{content:"\2601";} +.glyphicon-envelope:before{content:"\2709";} +.glyphicon-pencil:before{content:"\270f";} +.glyphicon-glass:before{content:"\e001";} +.glyphicon-music:before{content:"\e002";} +.glyphicon-search:before{content:"\e003";} +.glyphicon-heart:before{content:"\e005";} +.glyphicon-star:before{content:"\e006";} +.glyphicon-star-empty:before{content:"\e007";} +.glyphicon-user:before{content:"\e008";} +.glyphicon-film:before{content:"\e009";} +.glyphicon-th-large:before{content:"\e010";} +.glyphicon-th:before{content:"\e011";} +.glyphicon-th-list:before{content:"\e012";} +.glyphicon-ok:before{content:"\e013";} +.glyphicon-remove:before{content:"\e014";} +.glyphicon-zoom-in:before{content:"\e015";} +.glyphicon-zoom-out:before{content:"\e016";} +.glyphicon-off:before{content:"\e017";} +.glyphicon-signal:before{content:"\e018";} +.glyphicon-cog:before{content:"\e019";} +.glyphicon-trash:before{content:"\e020";} +.glyphicon-home:before{content:"\e021";} +.glyphicon-file:before{content:"\e022";} +.glyphicon-time:before{content:"\e023";} +.glyphicon-road:before{content:"\e024";} +.glyphicon-download-alt:before{content:"\e025";} +.glyphicon-download:before{content:"\e026";} +.glyphicon-upload:before{content:"\e027";} +.glyphicon-inbox:before{content:"\e028";} +.glyphicon-play-circle:before{content:"\e029";} +.glyphicon-repeat:before{content:"\e030";} +.glyphicon-refresh:before{content:"\e031";} +.glyphicon-list-alt:before{content:"\e032";} +.glyphicon-flag:before{content:"\e034";} +.glyphicon-headphones:before{content:"\e035";} +.glyphicon-volume-off:before{content:"\e036";} +.glyphicon-volume-down:before{content:"\e037";} +.glyphicon-volume-up:before{content:"\e038";} +.glyphicon-qrcode:before{content:"\e039";} +.glyphicon-barcode:before{content:"\e040";} +.glyphicon-tag:before{content:"\e041";} +.glyphicon-tags:before{content:"\e042";} +.glyphicon-book:before{content:"\e043";} +.glyphicon-print:before{content:"\e045";} +.glyphicon-font:before{content:"\e047";} +.glyphicon-bold:before{content:"\e048";} +.glyphicon-italic:before{content:"\e049";} +.glyphicon-text-height:before{content:"\e050";} +.glyphicon-text-width:before{content:"\e051";} +.glyphicon-align-left:before{content:"\e052";} +.glyphicon-align-center:before{content:"\e053";} +.glyphicon-align-right:before{content:"\e054";} +.glyphicon-align-justify:before{content:"\e055";} +.glyphicon-list:before{content:"\e056";} +.glyphicon-indent-left:before{content:"\e057";} +.glyphicon-indent-right:before{content:"\e058";} +.glyphicon-facetime-video:before{content:"\e059";} +.glyphicon-picture:before{content:"\e060";} +.glyphicon-map-marker:before{content:"\e062";} +.glyphicon-adjust:before{content:"\e063";} +.glyphicon-tint:before{content:"\e064";} +.glyphicon-edit:before{content:"\e065";} +.glyphicon-share:before{content:"\e066";} +.glyphicon-check:before{content:"\e067";} +.glyphicon-move:before{content:"\e068";} +.glyphicon-step-backward:before{content:"\e069";} +.glyphicon-fast-backward:before{content:"\e070";} +.glyphicon-backward:before{content:"\e071";} +.glyphicon-play:before{content:"\e072";} +.glyphicon-pause:before{content:"\e073";} +.glyphicon-stop:before{content:"\e074";} +.glyphicon-forward:before{content:"\e075";} +.glyphicon-fast-forward:before{content:"\e076";} +.glyphicon-step-forward:before{content:"\e077";} +.glyphicon-eject:before{content:"\e078";} +.glyphicon-chevron-left:before{content:"\e079";} +.glyphicon-chevron-right:before{content:"\e080";} +.glyphicon-plus-sign:before{content:"\e081";} +.glyphicon-minus-sign:before{content:"\e082";} +.glyphicon-remove-sign:before{content:"\e083";} +.glyphicon-ok-sign:before{content:"\e084";} +.glyphicon-question-sign:before{content:"\e085";} +.glyphicon-info-sign:before{content:"\e086";} +.glyphicon-screenshot:before{content:"\e087";} +.glyphicon-remove-circle:before{content:"\e088";} +.glyphicon-ok-circle:before{content:"\e089";} +.glyphicon-ban-circle:before{content:"\e090";} +.glyphicon-arrow-left:before{content:"\e091";} +.glyphicon-arrow-right:before{content:"\e092";} +.glyphicon-arrow-up:before{content:"\e093";} +.glyphicon-arrow-down:before{content:"\e094";} +.glyphicon-share-alt:before{content:"\e095";} +.glyphicon-resize-full:before{content:"\e096";} +.glyphicon-resize-small:before{content:"\e097";} +.glyphicon-exclamation-sign:before{content:"\e101";} +.glyphicon-gift:before{content:"\e102";} +.glyphicon-leaf:before{content:"\e103";} +.glyphicon-eye-open:before{content:"\e105";} +.glyphicon-eye-close:before{content:"\e106";} +.glyphicon-warning-sign:before{content:"\e107";} +.glyphicon-plane:before{content:"\e108";} +.glyphicon-random:before{content:"\e110";} +.glyphicon-comment:before{content:"\e111";} +.glyphicon-magnet:before{content:"\e112";} +.glyphicon-chevron-up:before{content:"\e113";} +.glyphicon-chevron-down:before{content:"\e114";} +.glyphicon-retweet:before{content:"\e115";} +.glyphicon-shopping-cart:before{content:"\e116";} +.glyphicon-folder-close:before{content:"\e117";} +.glyphicon-folder-open:before{content:"\e118";} +.glyphicon-resize-vertical:before{content:"\e119";} +.glyphicon-resize-horizontal:before{content:"\e120";} +.glyphicon-hdd:before{content:"\e121";} +.glyphicon-bullhorn:before{content:"\e122";} +.glyphicon-certificate:before{content:"\e124";} +.glyphicon-thumbs-up:before{content:"\e125";} +.glyphicon-thumbs-down:before{content:"\e126";} +.glyphicon-hand-right:before{content:"\e127";} +.glyphicon-hand-left:before{content:"\e128";} +.glyphicon-hand-up:before{content:"\e129";} +.glyphicon-hand-down:before{content:"\e130";} +.glyphicon-circle-arrow-right:before{content:"\e131";} +.glyphicon-circle-arrow-left:before{content:"\e132";} +.glyphicon-circle-arrow-up:before{content:"\e133";} +.glyphicon-circle-arrow-down:before{content:"\e134";} +.glyphicon-globe:before{content:"\e135";} +.glyphicon-tasks:before{content:"\e137";} +.glyphicon-filter:before{content:"\e138";} +.glyphicon-fullscreen:before{content:"\e140";} +.glyphicon-dashboard:before{content:"\e141";} +.glyphicon-heart-empty:before{content:"\e143";} +.glyphicon-link:before{content:"\e144";} +.glyphicon-phone:before{content:"\e145";} +.glyphicon-usd:before{content:"\e148";} +.glyphicon-gbp:before{content:"\e149";} +.glyphicon-sort:before{content:"\e150";} +.glyphicon-sort-by-alphabet:before{content:"\e151";} +.glyphicon-sort-by-alphabet-alt:before{content:"\e152";} +.glyphicon-sort-by-order:before{content:"\e153";} +.glyphicon-sort-by-order-alt:before{content:"\e154";} +.glyphicon-sort-by-attributes:before{content:"\e155";} +.glyphicon-sort-by-attributes-alt:before{content:"\e156";} +.glyphicon-unchecked:before{content:"\e157";} +.glyphicon-expand:before{content:"\e158";} +.glyphicon-collapse-down:before{content:"\e159";} +.glyphicon-collapse-up:before{content:"\e160";} +.glyphicon-log-in:before{content:"\e161";} +.glyphicon-flash:before{content:"\e162";} +.glyphicon-log-out:before{content:"\e163";} +.glyphicon-new-window:before{content:"\e164";} +.glyphicon-record:before{content:"\e165";} +.glyphicon-save:before{content:"\e166";} +.glyphicon-open:before{content:"\e167";} +.glyphicon-saved:before{content:"\e168";} +.glyphicon-import:before{content:"\e169";} +.glyphicon-export:before{content:"\e170";} +.glyphicon-send:before{content:"\e171";} +.glyphicon-floppy-disk:before{content:"\e172";} +.glyphicon-floppy-saved:before{content:"\e173";} +.glyphicon-floppy-remove:before{content:"\e174";} +.glyphicon-floppy-save:before{content:"\e175";} +.glyphicon-floppy-open:before{content:"\e176";} +.glyphicon-credit-card:before{content:"\e177";} +.glyphicon-transfer:before{content:"\e178";} +.glyphicon-cutlery:before{content:"\e179";} +.glyphicon-header:before{content:"\e180";} +.glyphicon-compressed:before{content:"\e181";} +.glyphicon-earphone:before{content:"\e182";} +.glyphicon-phone-alt:before{content:"\e183";} +.glyphicon-tower:before{content:"\e184";} +.glyphicon-stats:before{content:"\e185";} +.glyphicon-sd-video:before{content:"\e186";} +.glyphicon-hd-video:before{content:"\e187";} +.glyphicon-subtitles:before{content:"\e188";} +.glyphicon-sound-stereo:before{content:"\e189";} +.glyphicon-sound-dolby:before{content:"\e190";} +.glyphicon-sound-5-1:before{content:"\e191";} +.glyphicon-sound-6-1:before{content:"\e192";} +.glyphicon-sound-7-1:before{content:"\e193";} +.glyphicon-copyright-mark:before{content:"\e194";} +.glyphicon-registration-mark:before{content:"\e195";} +.glyphicon-cloud-download:before{content:"\e197";} +.glyphicon-cloud-upload:before{content:"\e198";} +.glyphicon-tree-conifer:before{content:"\e199";} +.glyphicon-tree-deciduous:before{content:"\e200";} +.glyphicon-briefcase:before{content:"\1f4bc";} +.glyphicon-calendar:before{content:"\1f4c5";} +.glyphicon-pushpin:before{content:"\1f4cc";} +.glyphicon-paperclip:before{content:"\1f4ce";} +.glyphicon-camera:before{content:"\1f4f7";} +.glyphicon-lock:before{content:"\1f512";} +.glyphicon-bell:before{content:"\1f514";} +.glyphicon-bookmark:before{content:"\1f516";} +.glyphicon-fire:before{content:"\1f525";} +.glyphicon-wrench:before{content:"\1f527";} diff --git a/tests/test_folium.py b/tests/test_folium.py index 0947da1350..dedebe5a41 100644 --- a/tests/test_folium.py +++ b/tests/test_folium.py @@ -53,6 +53,16 @@ def test_location_args(): assert m.location == [45.5236, -122.6750] +def test_glyphicons_css_is_vendored(): + # GH-1820: the glyphicon stylesheet must be folium's vendored glyphicons-only + # file, not netdna's ``bootstrap-glyphicons.css`` which despite its name ships + # a full Bootstrap-3 reset that leaked global ``body`` styles onto the page. + m = folium.Map() + html = m.get_root().render() + assert "folium/templates/glyphicons.css" in html + assert "netdna.bootstrapcdn.com" not in html + + class TestFolium: """Test class for the Folium library.""" From 59a744c0f30553b995bcc63fb13ac29da98682ce Mon Sep 17 00:00:00 2001 From: adarshsm <24850536+adarshsm@users.noreply.github.com> Date: Sat, 1 Aug 2026 11:36:01 +0530 Subject: [PATCH 2/2] Inline glyphicons stylesheet instead of linking a not-yet-published CDN file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous approach linked the vendored glyphicons stylesheet from `cdn.jsdelivr.net/gh/.../folium/templates/glyphicons.css`. That URL is served from the default branch, so it 404s until this PR is merged — a chicken-and-egg that fails the snapshot job (the `.glyphicon` rules never load) and, for a released package, would point at a mutable `main`. Inline the glyphicons-only rules into every `Map` as a ` + +