mirror of
https://github.com/zvx-echo6/navi.git
synced 2026-05-20 14:44:51 +02:00
feat(themes): add Tactical theme - NVG-compatible mil-spec display
Dark olive/charcoal base with sage green text and amber accents. Designed for night operations and eventual ATAK/iTAK integration: - Low contrast for night vision compatibility - Prominent olive-brown contours (topo-first design) - Subdued amber roads (preserves night vision better than blue/white) - Muted field-appropriate danger red - No bright blues or whites anywhere - Darkened/desaturated satellite mode Functional, not decorative. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
09369011ee
commit
04e15bf3dc
2 changed files with 327 additions and 0 deletions
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
import cleanTheme from './clean.js'
|
||||
import cyberpunkTheme from './cyberpunk.js'
|
||||
import tacticalTheme from './tactical.js'
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// UI CSS CUSTOM PROPERTIES
|
||||
|
|
@ -490,6 +491,7 @@ const themes = {
|
|||
fontImports: [],
|
||||
},
|
||||
cyberpunk: cyberpunkTheme,
|
||||
tactical: tacticalTheme,
|
||||
// Custom themes go here. Example:
|
||||
// 'midnight': {
|
||||
// id: 'midnight',
|
||||
|
|
|
|||
325
src/themes/tactical.js
Normal file
325
src/themes/tactical.js
Normal file
|
|
@ -0,0 +1,325 @@
|
|||
/**
|
||||
* Tactical Theme for Navi
|
||||
*
|
||||
* Military topographic map meets NVG-compatible night display. Dark olive/charcoal
|
||||
* base with muted sage greens for text — readable but low-signature. Subdued amber
|
||||
* for roads and primary actions (amber preserves night vision better than blue/white).
|
||||
* Danger in muted red. Low contrast by design — intentional for night use.
|
||||
*
|
||||
* Water is dark blue-gray, land is dark olive. Contours are PROMINENT in olive-brown —
|
||||
* this is a topo-first theme. The feel is a ruggedized field tablet displaying a
|
||||
* mil-spec moving map.
|
||||
*
|
||||
* Designed for field use and eventual ATAK/iTAK integration.
|
||||
* Functional, not decorative.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Map flavor colors - protomaps-themes-base schema
|
||||
* All 73 flat keys + pois + landcover nested objects
|
||||
*/
|
||||
const tacticalColors = {
|
||||
// Background & earth - dark olive
|
||||
background: "#0a0e0a",
|
||||
earth: "#0d110d",
|
||||
|
||||
// Land use areas - olive family
|
||||
park_a: "#141c14",
|
||||
park_b: "#182418",
|
||||
hospital: "#1a1818",
|
||||
industrial: "#121612",
|
||||
school: "#181814",
|
||||
wood_a: "#141c14",
|
||||
wood_b: "#182418",
|
||||
pedestrian: "#101410",
|
||||
scrub_a: "#161c16",
|
||||
scrub_b: "#182018",
|
||||
glacier: "#101814",
|
||||
sand: "#181816",
|
||||
beach: "#1c1c18",
|
||||
aerodrome: "#101410",
|
||||
runway: "#2a3028",
|
||||
water: "#0a1018",
|
||||
zoo: "#141c14",
|
||||
military: "#181c18",
|
||||
|
||||
// Tunnels - dark olive casings
|
||||
tunnel_other_casing: "#0d110d",
|
||||
tunnel_minor_casing: "#0d110d",
|
||||
tunnel_link_casing: "#0d110d",
|
||||
tunnel_major_casing: "#0d110d",
|
||||
tunnel_highway_casing: "#0d110d",
|
||||
tunnel_other: "#1a201a",
|
||||
tunnel_minor: "#1a201a",
|
||||
tunnel_link: "#2a3328",
|
||||
tunnel_major: "#4a4830",
|
||||
tunnel_highway: "#6a6028",
|
||||
|
||||
// Pier & buildings - olive
|
||||
pier: "#2a302a",
|
||||
buildings: "#1a221a",
|
||||
|
||||
// Roads & casings - olive to amber progression
|
||||
minor_service_casing: "#0d110d",
|
||||
minor_casing: "#0d110d",
|
||||
link_casing: "#0d110d",
|
||||
major_casing_late: "#0d110d",
|
||||
highway_casing_late: "#0d110d",
|
||||
other: "#2a3328",
|
||||
minor_service: "#2a3328",
|
||||
minor_a: "#3a4338",
|
||||
minor_b: "#2a3328",
|
||||
link: "#5a5838",
|
||||
major_casing_early: "#0d110d",
|
||||
major: "#8a7a40",
|
||||
highway_casing_early: "#0d110d",
|
||||
highway: "#c89030",
|
||||
railway: "#1a201a",
|
||||
boundaries: "#4a5a48",
|
||||
|
||||
// Waterway label - muted steel blue
|
||||
waterway_label: "#4a6a7a",
|
||||
|
||||
// Bridges - same olive/amber colors
|
||||
bridges_other_casing: "#101410",
|
||||
bridges_minor_casing: "#0d110d",
|
||||
bridges_link_casing: "#0d110d",
|
||||
bridges_major_casing: "#0d110d",
|
||||
bridges_highway_casing: "#0d110d",
|
||||
bridges_other: "#2a3328",
|
||||
bridges_minor: "#3a4338",
|
||||
bridges_link: "#5a5838",
|
||||
bridges_major: "#8a7a40",
|
||||
bridges_highway: "#c89030",
|
||||
|
||||
// Labels - sage green with DARK olive halos
|
||||
roads_label_minor: "#6a7a60",
|
||||
roads_label_minor_halo: "#0d110d",
|
||||
roads_label_major: "#8a9a80",
|
||||
roads_label_major_halo: "#0d110d",
|
||||
ocean_label: "#4a6a7a",
|
||||
peak_label: "#7a8a70",
|
||||
subplace_label: "#6a7a60",
|
||||
subplace_label_halo: "#0d110d",
|
||||
city_label: "#a0b090",
|
||||
city_label_halo: "#0d110d",
|
||||
state_label: "#5a6a50",
|
||||
state_label_halo: "#0d110d",
|
||||
country_label: "#7a8a70",
|
||||
address_label: "#6a7a60",
|
||||
address_label_halo: "#0d110d",
|
||||
|
||||
// POI icon colors - olive/amber/sage family, NO bright blues
|
||||
pois: {
|
||||
blue: "#5a7a6a",
|
||||
green: "#5a8a40",
|
||||
lapis: "#6a7a50",
|
||||
pink: "#8a6a60",
|
||||
red: "#aa3333",
|
||||
slategray: "#6a7a68",
|
||||
tangerine: "#c89030",
|
||||
turquoise: "#5a8a70",
|
||||
},
|
||||
|
||||
// Landcover fill colors - dark olive family
|
||||
landcover: {
|
||||
grassland: "rgba(20, 30, 18, 1)",
|
||||
barren: "rgba(24, 24, 20, 1)",
|
||||
urban_area: "rgba(16, 20, 16, 1)",
|
||||
farmland: "rgba(18, 26, 18, 1)",
|
||||
glacier: "rgba(16, 24, 20, 1)",
|
||||
scrub: "rgba(22, 28, 20, 1)",
|
||||
forest: "rgba(24, 36, 28, 1)",
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* UI CSS custom properties - tactical field display
|
||||
*/
|
||||
const tacticalUI = {
|
||||
"--font-sans": "'Inter', system-ui, -apple-system, sans-serif",
|
||||
"--font-mono": "'JetBrains Mono', ui-monospace, monospace",
|
||||
"--font-heading": "'Inter', system-ui, -apple-system, sans-serif",
|
||||
"--bg-base": "#0d110d",
|
||||
"--bg-raised": "#141a14",
|
||||
"--bg-overlay": "#1a2219",
|
||||
"--bg-input": "#101410",
|
||||
"--bg-inset": "#0a0e0a",
|
||||
"--bg-muted": "#182018",
|
||||
"--text-primary": "#a0b090",
|
||||
"--text-secondary": "#7a8a70",
|
||||
"--text-tertiary": "#5a6a50",
|
||||
"--text-inverse": "#0d110d",
|
||||
"--border": "#2a332a",
|
||||
"--border-subtle": "#1a221a",
|
||||
"--accent": "#c89030",
|
||||
"--accent-hover": "#d8a040",
|
||||
"--accent-muted": "#3a3020",
|
||||
"--tan": "#a09060",
|
||||
"--tan-muted": "#2a2818",
|
||||
"--pin-origin": "#c89030",
|
||||
"--pin-destination": "#8aaa70",
|
||||
"--pin-intermediate": "#6a7a60",
|
||||
"--pin-stroke": "#0d110d",
|
||||
"--status-success": "#5a8a40",
|
||||
"--status-warning": "#c89030",
|
||||
"--status-danger": "#aa3333",
|
||||
"--success": "#5a8a40",
|
||||
"--warning": "#c89030",
|
||||
"--warning-muted": "#2a2818",
|
||||
"--route-line": "#c89030",
|
||||
"--shadow": "0 2px 8px rgba(0, 0, 0, 0.5)",
|
||||
"--shadow-lg": "0 4px 16px rgba(0, 0, 0, 0.6)",
|
||||
}
|
||||
|
||||
/**
|
||||
* Overlay configuration - prominent contours, subdued everything else
|
||||
*/
|
||||
const tacticalOverlay = {
|
||||
hillshade: {
|
||||
exaggeration: 0.5,
|
||||
illuminationDirection: 315,
|
||||
shadowColor: "#000000",
|
||||
highlightColor: "#1a221a",
|
||||
},
|
||||
traffic: {
|
||||
opacity: 0.5,
|
||||
},
|
||||
contours: {
|
||||
opacityMod: 1.0,
|
||||
minorColor: "#6a5a38",
|
||||
minorOpacity: 0.6,
|
||||
minorWidth: { z11: 0.6, z14: 1.2 },
|
||||
intermediateColor: "#7a6a42",
|
||||
intermediateOpacity: 0.8,
|
||||
intermediateWidth: { z8: 1.0, z14: 1.5 },
|
||||
indexColor: "#8a7a4a",
|
||||
indexOpacity: 1.0,
|
||||
indexWidth: { z4: 1.5, z14: 2.2 },
|
||||
labelColor: "#8a7a58",
|
||||
labelHaloColor: "#0d110d",
|
||||
labelHaloWidth: 1.5,
|
||||
labelOpacity: 0.9,
|
||||
labelSize: 10,
|
||||
labelFont: ["Noto Sans Regular"],
|
||||
},
|
||||
contoursTest: {
|
||||
minorColor: "#5a5a38",
|
||||
intermediateColor: "#6a6a42",
|
||||
indexColor: "#7a7a4a",
|
||||
labelColor: "#8a8a58",
|
||||
},
|
||||
contoursTest10ft: {
|
||||
minorColor: "#4a5a38",
|
||||
intermediateColor: "#5a6a42",
|
||||
indexColor: "#6a7a4a",
|
||||
labelColor: "#7a8a58",
|
||||
},
|
||||
publicLands: {
|
||||
opacityMod: 0.6,
|
||||
fillWA: "#3a4030",
|
||||
fillNPS: "#2a3a28",
|
||||
fillUSFS: "#344030",
|
||||
fillBLM: "#4a4a38",
|
||||
fillFWS: "#2a4038",
|
||||
fillSTAT: "#344838",
|
||||
fillLOC: "#3a4a3a",
|
||||
fillDefault: "#3a3a30",
|
||||
fillOpacityWA: 0.25,
|
||||
fillOpacityNPS: 0.25,
|
||||
fillOpacityUSFS: 0.20,
|
||||
fillOpacityBLM: 0.18,
|
||||
fillOpacitySTAT: 0.22,
|
||||
fillOpacityLOC: 0.18,
|
||||
fillOpacityDefault: 0.12,
|
||||
outlineWA: "#4a5040",
|
||||
outlineNPS: "#3a4a38",
|
||||
outlineUSFS: "#445040",
|
||||
outlineBLM: "#5a5a48",
|
||||
outlineFWS: "#3a5048",
|
||||
outlineSTAT: "#445848",
|
||||
outlineLOC: "#4a5a4a",
|
||||
outlineDefault: "#4a4a40",
|
||||
outlineOpacityNPS: 0.6,
|
||||
outlineOpacityUSFS: 0.5,
|
||||
outlineOpacityDefault: 0.4,
|
||||
outlineWidth: { z4: 0.3, z8: 0.7, z12: 1.0 },
|
||||
labelColor: "#8aaa70",
|
||||
labelHaloColor: "#0d110d",
|
||||
labelHaloWidth: 1.5,
|
||||
labelOpacity: 0.8,
|
||||
labelSize: { z10: 10, z14: 12 },
|
||||
labelFont: ["Noto Sans Regular"],
|
||||
},
|
||||
usfsTrails: {
|
||||
roadsColor: "#8a7a40",
|
||||
roadsOpacity: 0.85,
|
||||
roadsWidth: { z10: 1.5, z14: 2.5, z16: 3.5 },
|
||||
trailsMotorized: "#c89030",
|
||||
trailsBicycle: "#a09040",
|
||||
trailsHiker: "#6a9a50",
|
||||
trailsDefault: "#8a8a50",
|
||||
trailsOpacity: 0.85,
|
||||
trailsWidth: { z10: 2.0, z14: 3.0, z16: 4.0 },
|
||||
trailsDash: [2, 1.5],
|
||||
roadsLabelColor: "#9a9a70",
|
||||
roadsLabelHaloColor: "#0d110d",
|
||||
roadsLabelHaloWidth: 1.5,
|
||||
roadsLabelOpacity: 0.85,
|
||||
roadsLabelSize: 11,
|
||||
trailsLabelColor: "#8a9a60",
|
||||
trailsLabelHaloColor: "#0d110d",
|
||||
trailsLabelHaloWidth: 1.5,
|
||||
trailsLabelOpacity: 0.85,
|
||||
trailsLabelSize: 11,
|
||||
labelFont: ["Noto Sans Regular"],
|
||||
hitWidth: 14,
|
||||
},
|
||||
blmTrails: {
|
||||
color4wdHigh: "#c89030",
|
||||
color4wdLow: "#a08030",
|
||||
colorAtv: "#aa5030",
|
||||
colorMotoSingle: "#8a7a50",
|
||||
color2wdLow: "#b09040",
|
||||
colorNonMech: "#6a9a50",
|
||||
colorDefault: "#8a8a50",
|
||||
colorSnow: "#6a8a7a",
|
||||
lineOpacity: 0.85,
|
||||
lineOpacityOther: 0.75,
|
||||
lineWidth: { z10: 2.0, z14: 3.0, z16: 4.0 },
|
||||
dashImproved: [4, 2],
|
||||
dashAggregate: [1, 2],
|
||||
dashSnow: [4, 2, 1, 2],
|
||||
dashOther: [4, 2, 1, 2, 1, 2],
|
||||
labelColor: "#9a9a70",
|
||||
labelHaloColor: "#0d110d",
|
||||
labelHaloWidth: 1.5,
|
||||
labelOpacity: 0.85,
|
||||
labelSize: 11,
|
||||
labelFont: ["Noto Sans Regular"],
|
||||
hitWidth: 14,
|
||||
},
|
||||
}
|
||||
|
||||
const tacticalSatellite = {
|
||||
opacity: 0.75,
|
||||
brightnessMin: 0.0,
|
||||
brightnessMax: 0.35,
|
||||
contrast: 0.0,
|
||||
saturation: -0.7,
|
||||
hueRotate: 0,
|
||||
}
|
||||
|
||||
const tacticalTheme = {
|
||||
id: "tactical",
|
||||
name: "Tactical",
|
||||
dark: true,
|
||||
swatch: ["#0d110d", "#c89030", "#a0b090"],
|
||||
fontImports: [],
|
||||
colors: tacticalColors,
|
||||
satellite: tacticalSatellite,
|
||||
overlay: tacticalOverlay,
|
||||
ui: tacticalUI,
|
||||
}
|
||||
|
||||
export default tacticalTheme
|
||||
Loading…
Add table
Add a link
Reference in a new issue