Styling System
Svelte OpenLayers provides a comprehensive CSS variable-based styling system that enables consistent, customizable design across all components while remaining framework-agnostic.
Overview
The styling system is built on modern CSS custom properties (CSS variables), providing:
- Complete design tokens for colors, spacing, typography, and shadows
- Dark mode support via both system preferences and manual controls
- Component-specific variables for fine-grained customization
- Framework independence with sensible defaults and fallbacks
- Zero runtime overhead - pure CSS solution
Quick Start
Basic Setup
Import the complete styling system in your application:
// In your app's entry point or root layout
import 'svelte-openlayers/styles.css';
That’s it! All components will now use the consistent styling system.
Import Options
You have several import options depending on your needs:
// Complete system (recommended)
import 'svelte-openlayers/styles.css';
// Variables only (no component styles)
import 'svelte-openlayers/styles/variables-only.css';
// Individual parts
import 'svelte-openlayers/styles/variables.css';
import 'svelte-openlayers/styles/components.css';
Core Design Tokens
Color System
The color system provides semantic colors that adapt to your design:
:root {
/* Primary colors */
--ol-color-primary: #4338ca;
--ol-color-primary-foreground: #ffffff;
--ol-color-secondary: #6b7280;
--ol-color-secondary-foreground: #ffffff;
/* Surface colors */
--ol-color-background: #ffffff;
--ol-color-foreground: #1f2937;
--ol-color-surface: #f9fafb;
--ol-color-surface-foreground: #374151;
/* Semantic colors */
--ol-color-muted: #f3f4f6;
--ol-color-muted-foreground: #6b7280;
--ol-color-accent: #e5e7eb;
--ol-color-accent-foreground: #1f2937;
/* Border & Input */
--ol-color-border: #d1d5db;
--ol-color-input: #e5e7eb;
--ol-color-ring: #6b7280;
}
Spacing Scale
A comprehensive spacing scale based on rem units:
:root {
--ol-space-0: 0;
--ol-space-px: 1px;
--ol-space-0-5: 0.125rem; /* 2px */
--ol-space-1: 0.25rem; /* 4px */
--ol-space-1-5: 0.375rem; /* 6px */
--ol-space-2: 0.5rem; /* 8px */
--ol-space-2-5: 0.625rem; /* 10px */
--ol-space-3: 0.75rem; /* 12px */
--ol-space-3-5: 0.875rem; /* 14px */
--ol-space-4: 1rem; /* 16px */
--ol-space-5: 1.25rem; /* 20px */
--ol-space-6: 1.5rem; /* 24px */
--ol-space-8: 2rem; /* 32px */
--ol-space-10: 2.5rem; /* 40px */
--ol-space-12: 3rem; /* 48px */
}
Typography
Comprehensive typography variables for consistent text styling:
:root {
/* Font sizes */
--ol-font-size-xs: 0.75rem; /* 12px */
--ol-font-size-sm: 0.875rem; /* 14px */
--ol-font-size-base: 1rem; /* 16px */
--ol-font-size-lg: 1.125rem; /* 18px */
--ol-font-size-xl: 1.25rem; /* 20px */
/* Line heights */
--ol-line-height-tight: 1.25;
--ol-line-height-normal: 1.5;
--ol-line-height-relaxed: 1.625;
/* Font weights */
--ol-font-weight-normal: 400;
--ol-font-weight-medium: 500;
--ol-font-weight-semibold: 600;
--ol-font-weight-bold: 700;
/* Font families */
--ol-font-family-sans:
ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
'Helvetica Neue', Arial, 'Noto Sans', sans-serif;
--ol-font-family-mono:
ui-monospace, SFMono-Regular, 'SF Mono', Consolas, 'Liberation Mono', Menlo, monospace;
}
Border Radius
Consistent border radius scale for rounded corners:
:root {
--ol-radius-none: 0;
--ol-radius-sm: 0.125rem; /* 2px */
--ol-radius: 0.25rem; /* 4px */
--ol-radius-md: 0.375rem; /* 6px */
--ol-radius-lg: 0.5rem; /* 8px */
--ol-radius-xl: 0.75rem; /* 12px */
--ol-radius-2xl: 1rem; /* 16px */
--ol-radius-full: 9999px;
}
Shadows
Elevation system using consistent shadow values:
:root {
--ol-shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--ol-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
--ol-shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--ol-shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
--ol-shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
}
Additional Design Tokens
Z-index scale, transitions, and opacity values:
:root {
/* Z-index scale */
--ol-z-10: 10;
--ol-z-20: 20;
--ol-z-50: 50;
--ol-z-100: 100;
--ol-z-500: 500;
--ol-z-1000: 1000;
/* Transitions */
--ol-transition-duration-fast: 150ms;
--ol-transition-duration-normal: 250ms;
--ol-transition-duration-slow: 350ms;
/* Opacity scale */
--ol-opacity-0: 0;
--ol-opacity-20: 0.2;
--ol-opacity-50: 0.5;
--ol-opacity-75: 0.75;
--ol-opacity-100: 1;
}
Component Customization
The styling system provides comprehensive variables for customizing all map components.
Map Container
Control the basic map container styling:
:root {
--ol-map-width: 100%;
--ol-map-height: 100%;
--ol-map-position: relative;
}
Map Controls
Base control styling that applies to all controls:
:root {
--ol-control-bg: rgba(255, 255, 255, 0.8);
--ol-control-border: var(--ol-color-border);
--ol-control-border-radius: var(--ol-radius);
--ol-control-shadow: var(--ol-shadow);
--ol-control-padding: var(--ol-space-2);
--ol-control-margin: var(--ol-space-2);
--ol-control-z-index: var(--ol-z-100);
}
Zoom Controls
Customize zoom button appearance:
:root {
/* Button dimensions and layout */
--ol-zoom-button-width: 1.5rem;
--ol-zoom-button-height: 1.5rem;
--ol-zoom-button-margin: var(--ol-space-0-5) 0;
/* Button styling */
--ol-zoom-button-bg: var(--ol-color-primary);
--ol-zoom-button-color: var(--ol-color-primary-foreground);
--ol-zoom-button-border: none;
--ol-zoom-button-radius: var(--ol-radius);
/* Typography */
--ol-zoom-button-font-size: var(--ol-font-size-sm);
--ol-zoom-button-font-weight: var(--ol-font-weight-medium);
}
Mouse Position Control
Customize the coordinate display:
:root {
--ol-mouse-position-bg: rgba(255, 255, 255, 0.9);
--ol-mouse-position-color: var(--ol-color-foreground);
--ol-mouse-position-padding: var(--ol-space-1-5) var(--ol-space-2-5);
--ol-mouse-position-border-radius: var(--ol-radius);
--ol-mouse-position-font-family: var(--ol-font-family-mono);
--ol-mouse-position-font-size: var(--ol-font-size-xs);
--ol-mouse-position-shadow: var(--ol-shadow-sm);
--ol-mouse-position-z-index: var(--ol-z-1000);
}
Attribution Control
Style the map attribution display:
:root {
--ol-attribution-bg: rgba(255, 255, 255, 0.7);
--ol-attribution-color: var(--ol-color-muted-foreground);
--ol-attribution-font-size: var(--ol-font-size-xs);
--ol-attribution-padding: var(--ol-space-1) var(--ol-space-2);
}
Tooltips
Comprehensive tooltip customization options:
:root {
/* Base tooltip styles */
--ol-tooltip-bg: rgba(255, 255, 255, 0.85);
--ol-tooltip-color: #181818;
--ol-tooltip-padding: var(--ol-space-2) var(--ol-space-3);
--ol-tooltip-border-radius: var(--ol-radius);
--ol-tooltip-font-size: var(--ol-font-size-sm);
--ol-tooltip-font-weight: var(--ol-font-weight-normal);
--ol-tooltip-line-height: var(--ol-line-height-normal);
--ol-tooltip-shadow: var(--ol-shadow-lg);
--ol-tooltip-max-width: 18.75rem; /* 300px */
--ol-tooltip-white-space: normal;
--ol-tooltip-word-wrap: break-word;
--ol-tooltip-z-index: var(--ol-z-100);
/* Tooltip transitions */
--ol-tooltip-transition-property: opacity, visibility;
--ol-tooltip-transition-duration: var(--ol-transition-duration-fast);
--ol-tooltip-transition-timing: var(--ol-transition-timing-ease-in-out);
/* Hover tooltip variant */
--ol-tooltip-hover-bg: rgba(255, 255, 255, 0.85);
--ol-tooltip-hover-color: #181818;
--ol-tooltip-hover-font-size: var(--ol-font-size-xs);
--ol-tooltip-hover-padding: var(--ol-space-1-5) var(--ol-space-2-5);
/* Select tooltip variant */
--ol-tooltip-select-bg: rgba(255, 255, 255, 0.96);
--ol-tooltip-select-color: var(--ol-color-foreground);
--ol-tooltip-select-border: 1px solid var(--ol-color-border);
--ol-tooltip-select-font-size: var(--ol-font-size-sm);
--ol-tooltip-select-padding: var(--ol-space-2-5) var(--ol-space-3-5);
--ol-tooltip-select-shadow: var(--ol-shadow-md);
--ol-tooltip-select-accent-color: var(--ol-color-primary);
}
Map Features
Default styling for map features (points, lines, polygons):
:root {
/* Feature stroke and fill */
--ol-feature-stroke-color: var(--ol-color-primary);
--ol-feature-stroke-width: 2;
--ol-feature-fill-color: rgba(67, 56, 202, 0.2);
/* Point features */
--ol-feature-point-radius: 5;
--ol-feature-point-fill: var(--ol-color-primary);
--ol-feature-point-stroke: var(--ol-color-primary-foreground);
--ol-feature-point-stroke-width: 2;
/* Overlays and layers */
--ol-overlay-z-index: var(--ol-z-100);
--ol-overlay-pointer-events: none;
--ol-layer-opacity: 1;
--ol-layer-visible: true;
--ol-layer-z-index: auto;
}
Feature Styling with TypeScript
Beyond CSS variables, Svelte OpenLayers provides TypeScript utilities for programmatically styling map features like points, lines, polygons, text, and icons. These utilities are located in src/lib/utils/styles.ts
.
Style Creation Functions
Circle Styles
Create circular point features with customizable appearance:
import { createCircleStyle } from 'svelte-openlayers/utils';
const pointStyle = createCircleStyle({
radius: 8,
fill: '#4338ca',
stroke: '#ffffff',
strokeWidth: 2,
displacement: [0, 0],
scale: 1
});
CircleStyleOptions Interface:
radius: number
- Circle radius in pixelsfill?: Color | string
- Fill color (optional)stroke?: Color | string
- Stroke color (optional)strokeWidth?: number
- Stroke width in pixels (optional)displacement?: number[]
- Offset from anchor point (optional)scale?: number | Size
- Scale factor or size (optional)
Text Styles
Add text labels to features with comprehensive typography control:
import { createTextStyle } from 'svelte-openlayers/utils';
const labelStyle = createTextStyle({
text: 'Feature Label',
font: '14px sans-serif',
offsetX: 0,
offsetY: -20,
textAlign: 'center',
fill: { color: '#1f2937' },
stroke: { color: '#ffffff', width: 2 },
backgroundFill: { color: 'rgba(255, 255, 255, 0.8)' },
padding: [4, 8, 4, 8]
});
TextStyleOptions Interface:
text: string
- Text content (required)font?: string
- Font specification (optional)offsetX?: number
- Horizontal offset (optional)offsetY?: number
- Vertical offset (optional)scale?: number | Size
- Text scale (optional)rotateWithView?: boolean
- Rotate with map view (optional)rotation?: number
- Text rotation in radians (optional)textAlign?: CanvasTextAlign
- Horizontal alignment (optional)justify?: 'left' | 'center' | 'right'
- Text justification (optional)textBaseline?: CanvasTextBaseline
- Vertical alignment (optional)fill?: FillStyleOptions
- Text fill styling (optional)stroke?: StrokeStyleOptions
- Text stroke styling (optional)backgroundFill?: FillStyleOptions
- Background fill (optional)backgroundStroke?: StrokeStyleOptions
- Background stroke (optional)padding?: number[]
- Background padding (optional)
Icon Styles
Use custom images as feature symbols:
import { createIconStyle } from 'svelte-openlayers/utils';
const markerStyle = createIconStyle({
src: '/icons/marker.png',
anchor: [0.5, 1], // Bottom-center anchor
scale: 0.8,
opacity: 0.9
});
IconStyleOptions Interface:
src: string
- Image URL (required)anchor?: number[]
- Anchor point as fraction [x, y] (optional)anchorXUnits?: 'fraction' | 'pixels'
- X anchor units (optional)anchorYUnits?: 'fraction' | 'pixels'
- Y anchor units (optional)color?: Color | string
- Tint color (optional)crossOrigin?: string | null
- CORS setting (optional)offset?: number[]
- Image offset in pixels (optional)displacement?: number[]
- Displacement from anchor (optional)opacity?: number
- Icon opacity 0-1 (optional)scale?: number | Size
- Icon scale factor (optional)width?: number
- Specific width in pixels (optional)height?: number
- Specific height in pixels (optional)rotation?: number
- Rotation in radians (optional)rotateWithView?: boolean
- Rotate with view (optional)size?: Size
- Icon size as [width, height] (optional)
Stroke and Fill Utilities
Create individual stroke and fill styles for composition:
import { createStrokeStyle, createFillStyle } from 'svelte-openlayers/utils';
const borderStroke = createStrokeStyle({
color: '#4338ca',
width: 3,
lineCap: 'round',
lineJoin: 'round',
lineDash: [5, 5]
});
const areaFill = createFillStyle({
color: 'rgba(67, 56, 202, 0.3)'
});
StrokeStyleOptions Interface:
color?: Color | string
- Stroke color (optional)width?: number
- Line width in pixels (optional)lineCap?: CanvasLineCap
- Line cap style (optional)lineJoin?: CanvasLineJoin
- Line join style (optional)lineDash?: number[]
- Dash pattern array (optional)lineDashOffset?: number
- Dash pattern offset (optional)miterLimit?: number
- Miter limit for joins (optional)
FillStyleOptions Interface:
color?: Color | string
- Fill color (optional)
Composite Styles
Create complex styles combining multiple elements:
import { createStyle } from 'svelte-openlayers/utils';
const complexStyle = createStyle({
fill: { color: 'rgba(67, 56, 202, 0.2)' },
stroke: { color: '#4338ca', width: 2 },
image: {
radius: 6,
fill: '#4338ca',
stroke: '#ffffff',
strokeWidth: 2
},
text: {
text: 'Label',
fill: { color: '#1f2937' },
stroke: { color: '#ffffff', width: 1 }
}
});
Common Customization Patterns
Brand Colors
Apply your brand colors across all components:
:root {
/* Use your brand colors */
--ol-color-primary: #059669; /* Emerald */
--ol-color-primary-foreground: white;
/* Adjust related colors */
--ol-zoom-button-bg: var(--ol-color-primary);
--ol-tooltip-select-accent-color: var(--ol-color-primary);
}
Compact Spacing
Create a more compact UI by adjusting the spacing scale:
:root {
/* Reduce all spacing proportionally */
--ol-space-1: 0.125rem; /* 2px */
--ol-space-2: 0.25rem; /* 4px */
--ol-space-3: 0.375rem; /* 6px */
--ol-space-4: 0.5rem; /* 8px */
}
Rounded Design
Make everything more rounded:
:root {
--ol-radius: 0.5rem;
--ol-zoom-button-radius: 9999px; /* Fully rounded */
--ol-tooltip-border-radius: 1rem;
}
Integration Examples
With Tailwind CSS
Integrate with Tailwind’s design system:
@import 'tailwindcss/base';
@import 'svelte-openlayers/styles/variables.css';
:root {
--ol-color-primary: theme('colors.blue.600');
--ol-color-border: theme('colors.gray.200');
--ol-radius: theme('borderRadius.lg');
--ol-shadow-md: theme('boxShadow.md');
}
@import 'svelte-openlayers/styles/components.css';
With Bootstrap
Use Bootstrap’s variables:
@import 'bootstrap/scss/bootstrap';
@import 'svelte-openlayers/styles/variables.css';
:root {
--ol-color-primary: var(--bs-primary);
--ol-color-secondary: var(--bs-secondary);
--ol-font-family-sans: var(--bs-font-sans-serif);
}
@import 'svelte-openlayers/styles/components.css';
With Custom Design System
Map your existing design tokens:
:root {
/* Map your tokens to OpenLayers variables */
--ol-color-primary: var(--brand-primary);
--ol-color-background: var(--app-background);
--ol-space-1: var(--spacing-xs);
--ol-space-2: var(--spacing-sm);
--ol-radius: var(--border-radius-base);
}
Advanced Techniques
Scoped Customization
Apply different styles to different map instances:
<div class="ocean-theme">
<Map.Root>
<!-- Uses ocean theme -->
</Map.Root>
</div>
<div class="forest-theme">
<Map.Root>
<!-- Uses forest theme -->
</Map.Root>
</div>
<style>
.ocean-theme {
--ol-color-primary: #0891b2;
--ol-tooltip-bg: rgba(8, 145, 178, 0.9);
}
.forest-theme {
--ol-color-primary: #059669;
--ol-tooltip-bg: rgba(5, 150, 105, 0.9);
}
</style>
Dynamic Theming
Change themes programmatically:
<script>
function applyTheme(theme) {
const root = document.documentElement;
switch (theme) {
case 'ocean':
root.style.setProperty('--ol-color-primary', '#0891b2');
root.style.setProperty('--ol-color-secondary', '#06b6d4');
break;
case 'sunset':
root.style.setProperty('--ol-color-primary', '#f97316');
root.style.setProperty('--ol-color-secondary', '#fb923c');
break;
case 'midnight':
root.style.setProperty('--ol-color-primary', '#6366f1');
root.style.setProperty('--ol-color-secondary', '#8b5cf6');
break;
}
}
</script>
<select onchange={(e) => applyTheme(e.target.value)}>
<option value="ocean">Ocean</option>
<option value="sunset">Sunset</option>
<option value="midnight">Midnight</option>
</select>
Responsive Styling
Adjust styles based on screen size:
/* Mobile-first responsive adjustments */
@media (max-width: 640px) {
:root {
--ol-tooltip-max-width: 250px;
--ol-space-2: 0.375rem; /* Tighter spacing on mobile */
--ol-font-size-sm: 0.8125rem;
}
}
@media (min-width: 1024px) {
:root {
--ol-space-2: 0.625rem; /* More spacious on desktop */
--ol-tooltip-max-width: 400px;
}
}