diff --git a/assets/js/app.js b/assets/js/app.js
index 81f84ec..8d8a764 100644
--- a/assets/js/app.js
+++ b/assets/js/app.js
@@ -20,7 +20,8 @@ import "phoenix_html";
// Establish Phoenix Socket and LiveView configuration.
import { Socket } from "phoenix";
import { LiveSocket } from "phoenix_live_view";
-import topbar from "topbar";
+// topbar is loaded globally from vendor bundle
+const topbar = window.topbar;
// Sentry initialization happens via the loader script in the HTML
// Configure additional Sentry settings if needed
diff --git a/assets/js/features/weather_charts.ts b/assets/js/features/weather_charts.ts
index e9cc2ce..a3d8e37 100644
--- a/assets/js/features/weather_charts.ts
+++ b/assets/js/features/weather_charts.ts
@@ -1,13 +1,10 @@
-// Import Chart.js and date adapter
-import Chart from 'chart.js/auto';
-import 'chartjs-adapter-date-fns';
+// Chart.js and date adapter are loaded globally from vendor bundle
+const Chart = window.Chart;
+
import type { ChartConfiguration, ChartType } from 'chart.js';
import type { WeatherChartDataset, YAxisOptions } from '../types/chart-types';
import type { HandleEventFunction } from '../types/events';
-// Make Chart globally available for legacy code
-window.Chart = Chart;
-
// Declare global Chart object
declare global {
interface Window {
diff --git a/assets/js/map.ts b/assets/js/map.ts
index 1d06fee..d957844 100644
--- a/assets/js/map.ts
+++ b/assets/js/map.ts
@@ -12,19 +12,10 @@ import type { HeatLayer, MarkerClusterGroup, OverlappingMarkerSpiderfier, Marker
import type { APRSMarker } from './types/marker-extensions';
import type { BaseEventPayload } from './types/events';
-// Import Leaflet and plugins
-import L from 'leaflet';
-import 'leaflet.heat';
-import 'leaflet.markercluster';
-import 'overlapping-marker-spiderfier-leaflet';
+// Leaflet and plugins are loaded globally from vendor bundle
+const L = window.L;
-// Import CSS files
-import 'leaflet/dist/leaflet.css';
-import 'leaflet.markercluster/dist/MarkerCluster.css';
-import 'leaflet.markercluster/dist/MarkerCluster.Default.css';
-
-// Make Leaflet globally available for legacy code
-window.L = L;
+// CSS files are bundled in vendor.js
// The OverlappingMarkerSpiderfier is attached to window by the UMD module
declare global {
diff --git a/assets/js/vendor.js b/assets/js/vendor.js
new file mode 100644
index 0000000..c126a45
--- /dev/null
+++ b/assets/js/vendor.js
@@ -0,0 +1,28 @@
+// Vendor bundle for all third-party dependencies
+// This file bundles all npm packages to avoid resolution issues during Docker builds
+
+// Import CSS files first
+import 'leaflet/dist/leaflet.css';
+import 'leaflet.markercluster/dist/MarkerCluster.css';
+import 'leaflet.markercluster/dist/MarkerCluster.Default.css';
+
+// Export Leaflet and plugins
+import * as L from 'leaflet';
+import 'leaflet.heat';
+import 'leaflet.markercluster';
+import 'overlapping-marker-spiderfier-leaflet';
+
+// Export Chart.js and adapter
+import Chart from 'chart.js/auto';
+import 'chartjs-adapter-date-fns';
+
+// Export topbar
+import topbar from 'topbar';
+
+// Make libraries available globally
+window.L = L;
+window.Chart = Chart;
+window.topbar = topbar;
+
+// Export for ES modules
+export { L, Chart, topbar };
\ No newline at end of file
diff --git a/config/config.exs b/config/config.exs
index 1bce9d3..4252309 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -85,6 +85,12 @@ config :esbuild,
~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/* --loader:.css=css --loader:.png=file --loader:.svg=file),
cd: Path.expand("../assets", __DIR__),
env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
+ ],
+ vendor: [
+ args:
+ ~w(js/vendor.js --bundle --target=es2017 --outdir=../priv/static/assets --minify --loader:.css=css --loader:.png=file --loader:.svg=file),
+ cd: Path.expand("../assets", __DIR__),
+ env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
]
config :gettext, :plural_forms, GettextPseudolocalize.Plural
diff --git a/lib/aprsme_web/components/layouts/root.html.heex b/lib/aprsme_web/components/layouts/root.html.heex
index 0ea1280..c991cd5 100644
--- a/lib/aprsme_web/components/layouts/root.html.heex
+++ b/lib/aprsme_web/components/layouts/root.html.heex
@@ -26,6 +26,8 @@
{assigns[:page_title] || "Aprs"}
+