diff --git a/.changeset/map-markers.md b/.changeset/map-markers.md
new file mode 100644
index 000000000..d49fc4b12
--- /dev/null
+++ b/.changeset/map-markers.md
@@ -0,0 +1,7 @@
+---
+"@tabler/core": minor
+"@tabler/preview": minor
+"@tabler/docs": patch
+---
+
+Added `.map-marker` with dot, pin and color variants and `.map-popup-card` for MapLibre maps to `tabler-vendors.css`.
diff --git a/core/package.json b/core/package.json
index e5d718d4f..7b5aece3d 100644
--- a/core/package.json
+++ b/core/package.json
@@ -196,19 +196,19 @@
},
{
"path": "./dist/css/tabler-vendors.css",
- "maxSize": "4.5 kB"
+ "maxSize": "5 kB"
},
{
"path": "./dist/css/tabler-vendors.min.css",
- "maxSize": "4 kB"
- },
- {
- "path": "./dist/css/tabler-vendors.rtl.css",
"maxSize": "4.5 kB"
},
+ {
+ "path": "./dist/css/tabler-vendors.rtl.css",
+ "maxSize": "5 kB"
+ },
{
"path": "./dist/css/tabler-vendors.rtl.min.css",
- "maxSize": "4 kB"
+ "maxSize": "4.5 kB"
},
{
"path": "./dist/js/tabler.js",
diff --git a/core/scss/_variables.scss b/core/scss/_variables.scss
index dd97a60f7..d078ece95 100644
--- a/core/scss/_variables.scss
+++ b/core/scss/_variables.scss
@@ -1279,6 +1279,19 @@ $ribbon-bookmark-border-size: 1rem !default;
$ribbon-bookmark-notch-width: 0.5rem !default;
// scss-docs-end ribbon-variables
+// scss-docs-start map-marker-variables
+$map-marker-size: 1.75rem !default;
+$map-marker-dot-size: 0.875rem !default;
+$map-marker-bg: var(--primary) !default;
+$map-marker-color: var(--primary-fg) !default;
+$map-marker-border-width: 2px !default;
+$map-marker-border-color: var(--bg-surface) !default;
+$map-marker-font-size: $h5-font-size !default;
+$map-marker-icon-size: 1rem !default;
+$map-marker-shadow: drop-shadow(0 1px 2px rgba($black, 0.3)) !default;
+$map-popup-card-width: 16rem !default;
+// scss-docs-end map-marker-variables
+
// Accordion
// scss-docs-start accordion-variables
$accordion-padding-y: 1rem !default;
diff --git a/core/scss/tests/__snapshots__/css-var-prefix.test.mjs.snap b/core/scss/tests/__snapshots__/css-var-prefix.test.mjs.snap
index 88e717e1b..92074577e 100644
--- a/core/scss/tests/__snapshots__/css-var-prefix.test.mjs.snap
+++ b/core/scss/tests/__snapshots__/css-var-prefix.test.mjs.snap
@@ -37,17 +37,21 @@ exports[`css custom-property prefixing > leaves vendor-owned names alone and pre
"--plyr-color-main",
"--tblr-active-bg",
"--tblr-azure",
+ "--tblr-azure-fg",
"--tblr-backdrop-bg",
"--tblr-bg-forms",
"--tblr-bg-surface",
"--tblr-bg-surface-inverted",
"--tblr-bg-surface-secondary",
"--tblr-black",
+ "--tblr-blue",
+ "--tblr-blue-fg",
"--tblr-body-bg",
"--tblr-body-color",
"--tblr-border-color",
"--tblr-border-color-translucent",
"--tblr-border-radius",
+ "--tblr-border-radius-pill",
"--tblr-border-style",
"--tblr-border-width",
"--tblr-card-bg",
@@ -61,7 +65,12 @@ exports[`css custom-property prefixing > leaves vendor-owned names alone and pre
"--tblr-control-padding-x",
"--tblr-control-padding-y",
"--tblr-control-placeholder-color",
+ "--tblr-cyan",
+ "--tblr-cyan-fg",
"--tblr-danger",
+ "--tblr-danger-fg",
+ "--tblr-dark",
+ "--tblr-dark-fg",
"--tblr-focus-ring",
"--tblr-focus-ring-color",
"--tblr-focus-ring-offset",
@@ -79,26 +88,56 @@ exports[`css custom-property prefixing > leaves vendor-owned names alone and pre
"--tblr-form-valid-border-color",
"--tblr-gray-500",
"--tblr-green",
+ "--tblr-green-fg",
"--tblr-icon-size",
+ "--tblr-indigo",
+ "--tblr-indigo-fg",
"--tblr-info",
+ "--tblr-info-fg",
"--tblr-light",
+ "--tblr-light-fg",
+ "--tblr-lime",
+ "--tblr-lime-fg",
+ "--tblr-map-marker-bg",
+ "--tblr-map-marker-border-color",
+ "--tblr-map-marker-border-width",
+ "--tblr-map-marker-color",
+ "--tblr-map-marker-font-size",
+ "--tblr-map-marker-icon-size",
+ "--tblr-map-marker-size",
+ "--tblr-muted",
+ "--tblr-muted-fg",
"--tblr-orange",
+ "--tblr-orange-fg",
+ "--tblr-pink",
+ "--tblr-pink-fg",
"--tblr-primary",
"--tblr-primary-200",
+ "--tblr-primary-fg",
"--tblr-primary-lt",
"--tblr-purple",
+ "--tblr-purple-fg",
+ "--tblr-red",
+ "--tblr-red-fg",
"--tblr-secondary",
"--tblr-secondary-bg",
+ "--tblr-secondary-fg",
"--tblr-shadow-card",
"--tblr-shadow-color",
"--tblr-shadow-dropdown",
"--tblr-shadow-input",
"--tblr-signal-color",
"--tblr-success",
+ "--tblr-success-fg",
+ "--tblr-teal",
+ "--tblr-teal-fg",
"--tblr-tertiary",
"--tblr-text-inverted",
+ "--tblr-warning",
+ "--tblr-warning-fg",
"--tblr-white",
"--tblr-yellow",
+ "--tblr-yellow-fg",
"--ts-pr-caret",
"--ts-pr-clear-button",
]
diff --git a/core/scss/vendor/_maplibre.scss b/core/scss/vendor/_maplibre.scss
index 562d9fc08..bd72aa78c 100644
--- a/core/scss/vendor/_maplibre.scss
+++ b/core/scss/vendor/_maplibre.scss
@@ -122,6 +122,91 @@
border-left-color: var(--bg-surface);
}
+// Popup that holds a card-like block: an image on top, padded content below
+.map-popup-card {
+ .maplibregl-popup-content {
+ width: #{$map-popup-card-width};
+ padding: 0;
+ overflow: hidden;
+ }
+
+ .maplibregl-popup-close-button {
+ color: var(--body-color);
+ background-color: var(--bg-surface);
+ }
+}
+
+// Marker drawn in HTML. MapLibre moves the marker with `transform`, so the
+// shape is drawn by the pseudo-element and the root keeps no transform.
+.map-marker {
+ // scss-docs-start map-marker-css-vars
+ --map-marker-size: #{$map-marker-size};
+ --map-marker-bg: #{$map-marker-bg};
+ --map-marker-color: #{$map-marker-color};
+ --map-marker-border-width: #{$map-marker-border-width};
+ --map-marker-border-color: #{$map-marker-border-color};
+ --map-marker-font-size: #{$map-marker-font-size};
+ --map-marker-icon-size: #{$map-marker-icon-size};
+ // scss-docs-end map-marker-css-vars
+ position: relative;
+ z-index: 0;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ min-width: var(--map-marker-size);
+ min-height: var(--map-marker-size);
+ padding: var(--map-marker-border-width);
+ font-size: var(--map-marker-font-size);
+ font-weight: var(--font-weight-medium);
+ line-height: 1;
+ color: var(--map-marker-color);
+ cursor: pointer;
+ filter: #{$map-marker-shadow};
+
+ &::before {
+ position: absolute;
+ inset: 0;
+ z-index: -1;
+ content: '';
+ background: var(--map-marker-bg);
+ border: var(--map-marker-border-width) solid var(--map-marker-border-color);
+ @include border-radius(var(--border-radius-pill));
+ }
+
+ .icon {
+ --icon-size: var(--map-marker-icon-size);
+ }
+}
+
+.map-marker-dot {
+ --map-marker-size: #{$map-marker-dot-size};
+}
+
+// Teardrop: a square with one sharp corner, turned to point down. Its tip
+// reaches 20.7% below the circle, so the root is that much taller.
+.map-marker-pin {
+ box-sizing: content-box;
+ width: var(--map-marker-size);
+ min-width: 0;
+ height: var(--map-marker-size);
+ min-height: 0;
+ padding: 0 0 calc(var(--map-marker-size) * 0.207);
+
+ &::before {
+ bottom: auto;
+ height: var(--map-marker-size);
+ @include border-radius(50% 50% 50% 0);
+ transform: rotate(-45deg);
+ }
+}
+
+@each $name, $color in $theme-colors {
+ .map-marker-#{$name} {
+ --map-marker-bg: var(--#{$name});
+ --map-marker-color: var(--#{$name}-fg);
+ }
+}
+
// The control icons are black SVG images
@if $enable-dark-mode {
@include color-mode(dark) {
diff --git a/docs/content/ui/components/map.mdx b/docs/content/ui/components/map.mdx
index be3b2fa86..77ee87be3 100644
--- a/docs/content/ui/components/map.mdx
+++ b/docs/content/ui/components/map.mdx
@@ -4,17 +4,31 @@ summary: A map shows places and markers on an interactive world map. Tabler prov
description: Show interactive maps with markers using MapLibre GL JS and free map tiles. No account or access token is needed.
related: [/ui/plugins/vector-map]
docs-libs: [maplibre-gl]
-source: core/scss/_utilities.scss
+source: core/scss/vendor/_maplibre.scss
+css-vars: map-marker
classnames:
component:
- class: ratio
- desc: Wrapper that keeps the embed at a fixed aspect ratio
+ desc: Wrapper that keeps the map at a fixed aspect ratio
+ - class: map-marker
+ desc: Marker drawn in HTML, a circle that can hold an icon, a count or an avatar
+ modifier:
+ - class: map-marker-dot
+ desc: Small dot with no content
+ - class: map-marker-pin
+ desc: Teardrop pin that points down, used with the bottom anchor
+ - class: map-popup-card
+ desc: Popup with no padding and a fixed width, for an image above the text
+ color:
+ - class: map-marker-{color}
+ desc: Any theme color, for example map-marker-red or map-marker-green
size:
- class: ratio-{ratio}
desc: The aspect to hold, for example ratio-16x9 or ratio-21x9
---
import Example from '@components/Example.astro';
import Map from '@shared/components/demo/Map.astro';
+import Icon from '@ui/Icon.astro';
import { Code } from 'astro:components';
import { site } from '@shared/lib/site.ts';
@@ -122,6 +136,69 @@ cities.forEach(function (coords) {
The `color` option is written to the `fill` attribute of the marker icon. A CSS variable works there, so `var(--tblr-primary)` keeps the markers in your theme color, including after a theme change.
+### Custom markers
+
+To draw a marker in HTML, create an element with the `.map-marker` class and pass it as `element`. The marker is a circle in the primary color. Put an icon, a short count or an avatar inside it.
+
+```js
+const element = document.createElement('span')
+element.className = 'map-marker map-marker-green'
+element.textContent = '12'
+
+new maplibregl.Marker({ element: element }).setLngLat([13.35, 52.512]).addTo(map)
+```
+
+Add `.map-marker-dot` for a small dot, or `.map-marker-pin` for a pin that points down. A pin needs `anchor: 'bottom'`, so its tip sits on the place. Change the color with `.map-marker-{color}`.
+
+```js
+const pin = document.createElement('span')
+pin.className = 'map-marker map-marker-pin map-marker-red'
+
+new maplibregl.Marker({ element: pin, anchor: 'bottom' }).setLngLat([13.47, 52.518]).addTo(map)
+```
+
+
.map-marker: a dot, a circle with an icon or a count, a pin, or an avatar.<template> next to the marker. .map-popup-card makes room for an image.draggable: true to let people move a marker, for example to pick an address.