18 Commits

Author SHA1 Message Date
Alexis
e43baa13da Merge branch 'dev' 2025-07-17 23:19:29 +02:00
Alexis
b243c3129d Fixed some accessibility issues 2025-07-17 23:19:24 +02:00
Alexis
3f60672bb0 Fixed some SEO data 2025-07-17 23:11:02 +02:00
Alexis
5792d7d15f Sample missing icons 2025-07-17 22:12:00 +02:00
Alexis
91ea78c632 Updated package 2025-07-17 22:11:53 +02:00
Alexis
961fd0d198 Updated package 2025-07-14 18:49:30 +02:00
Alexis
1cba7b398b Updated package 2025-06-25 16:16:01 +02:00
Alexis
05d3bca10b Version bump 2025-06-22 14:35:09 +02:00
Alexis
3acfc6db47 Merge branch 'dev' 2025-06-22 14:29:08 +02:00
Alexis
6c350bf2d5 Removed new item for mine map 2025-06-22 14:28:26 +02:00
Alexis
712d408736 Added theme switcher 2025-06-21 21:33:25 +02:00
Alexis
bfdd433a5f Added dark mode ui for leaflet controls 2025-06-21 14:32:23 +02:00
Alexis
ac6558392c Fixed some more UI with dark mode 2025-06-21 11:49:15 +02:00
Alexis
484fe3b031 Fixed search results in dark mode 2025-06-21 11:33:20 +02:00
Alexis
c88133a9a3 Moved some variables to root 2025-06-20 20:06:07 +02:00
Alexis
1cb77dd6f0 Version bump 2025-06-20 17:20:42 +02:00
Alexis
a2fe47210f Updated packages 2025-06-20 17:20:29 +02:00
Alexis
38dfd3c952 Updated readme 2025-06-20 17:11:59 +02:00
39 changed files with 1083 additions and 797 deletions

View File

@@ -1,5 +1,15 @@
# Leim Maps
Repository for interactive maps using Leim's Wiki data
🗺️ A simple Astro static site to display custom maps with Leaflet. Includes sample images with maps from my own world.
## Demo
Live demo available at https://maps.alexcreates.fr
## Features
* Add your own markers for cities, NPCs, or quests
* Search through markers
* Create multiple maps for different continents or countries
* Measure distances between two points
* Custom client-side markers
## Contributing
I'm not currently accepting pull-requests for this project, but if you have a suggestion, feel free to contact me !
## Notes
As someone with some degree of with [aphantasia](https://en.wikipedia.org/wiki/Aphantasia), I struggle with visualization and immersion, and DMing in general. If you find yourself in the same situation, I hope my little thing made your life easier !

View File

@@ -1,7 +1,7 @@
{
"name": "leim-maps",
"type": "module",
"version": "1.4.10",
"version": "1.4.12",
"scripts": {
"dev": "astro dev",
"start": "astro dev",
@@ -13,13 +13,13 @@
"@astrojs/sitemap": "^3.4.1",
"@astrojs/vue": "^5.1.0",
"@nanostores/persistent": "1.0.0",
"@nanostores/vue": "^1.0.0",
"@types/leaflet": "^1.9.18",
"@vueuse/core": "^13.3.0",
"astro": "^5.9.3",
"@nanostores/vue": "^1.0.1",
"@types/leaflet": "^1.9.20",
"@vueuse/core": "^13.5.0",
"astro": "^5.12.0",
"nanostores": "^1.0.1",
"radix-vue": "^1.9.17",
"vue": "^3.5.16"
"vue": "^3.5.17"
},
"devDependencies": {
"sass": "^1.89.2"

1295
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="#FFF"><path d="M140,180a12,12,0,1,1-12-12A12,12,0,0,1,140,180ZM128,72c-22.06,0-40,16.15-40,36v4a8,8,0,0,0,16,0v-4c0-11,10.77-20,24-20s24,9,24,20-10.77,20-24,20a8,8,0,0,0-8,8v8a8,8,0,0,0,16,0v-.72c18.24-3.35,32-17.9,32-35.28C168,88.15,150.06,72,128,72Zm104,56A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128Z"></path></svg>

After

Width:  |  Height:  |  Size: 440 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#FFF" viewBox="0 0 256 256"><path d="M235.32,73.37,182.63,20.69a16,16,0,0,0-22.63,0L20.68,160a16,16,0,0,0,0,22.63l52.69,52.68a16,16,0,0,0,22.63,0L235.32,96A16,16,0,0,0,235.32,73.37ZM84.68,224,32,171.31l32-32,26.34,26.35a8,8,0,0,0,11.32-11.32L75.31,128,96,107.31l26.34,26.35a8,8,0,0,0,11.32-11.32L107.31,96,128,75.31l26.34,26.35a8,8,0,0,0,11.32-11.32L139.31,64l32-32L224,84.69Z"></path></svg>

After

Width:  |  Height:  |  Size: 461 B

1
public/icon/ruler.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#020617" viewBox="0 0 256 256"><path d="M235.32,73.37,182.63,20.69a16,16,0,0,0-22.63,0L20.68,160a16,16,0,0,0,0,22.63l52.69,52.68a16,16,0,0,0,22.63,0L235.32,96A16,16,0,0,0,235.32,73.37ZM84.68,224,32,171.31l32-32,26.34,26.35a8,8,0,0,0,11.32-11.32L75.31,128,96,107.31l26.34,26.35a8,8,0,0,0,11.32-11.32L107.31,96,128,75.31l26.34,26.35a8,8,0,0,0,11.32-11.32L139.31,64l32-32L224,84.69Z"></path></svg>

After

Width:  |  Height:  |  Size: 464 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><path d="M224,104a8,8,0,0,1-13.66,5.66L184,83.31l-42.34,42.35a8,8,0,0,1-11.32-11.32L172.69,72,146.34,45.66A8,8,0,0,1,152,32h64a8,8,0,0,1,8,8Zm-40,24a8,8,0,0,0-8,8v72H48V80h72a8,8,0,0,0,0-16H48A16,16,0,0,0,32,80V208a16,16,0,0,0,16,16H176a16,16,0,0,0,16-16V136A8,8,0,0,0,184,128Z" fill="#FFF" /></svg>

After

Width:  |  Height:  |  Size: 405 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -1,7 +1,7 @@
// Animations on load
@media screen and (prefers-reduced-motion: no-preference) {
.appear-from-top {
animation: fadeIn .2s cubic-bezier(0.785, 0.135, 0.15, 0.86);
animation: fadeIn .2s var(--timing-function);
}
@keyframes fadeIn {

View File

@@ -5,12 +5,13 @@
gap: .5rem;
font-size: .9em;
font-weight: 500;
color: var(--foreground);
border: 1px solid transparent;
border-radius: .2rem;
border-radius: var(--border-radius);
cursor: pointer;
transition-property: color, background-color, border-color;
transition-duration: .2s;
transition-timing-function: cubic-bezier(0.785, 0.135, 0.15, 0.86);
transition-timing-function: var(--timing-function);
&:disabled {
cursor: not-allowed;
@@ -18,40 +19,40 @@
&.btn-secondary {
&:not(:disabled) {
background-color: var(--slate-200);
background-color: color-mix(in srgb, var(--foreground) 20%, var(--background));
&:hover,
&:focus-visible {
color: var(--white);
background-color: var(--blue-500);
color: var(--background);
background-color: var(--accent);
}
}
&:disabled {
color: var(--slate-500);
background-color: var(--slate-100);
color: color-mix(in srgb, var(--foreground) 50%, var(--background));
background-color: color-mix(in srgb, var(--foreground) 10%, var(--background));
}
}
&.btn-info {
&:not(:disabled) {
background-color: var(--slate-200);
background-color: color-mix(in srgb, var(--foreground) 20%, var(--background));
&:hover,
&:focus-visible {
color: var(--white);
background-color: var(--blue-500);
color: var(--background);
background-color: var(--accent);
}
}
}
&.btn-danger {
&:not(:disabled) {
background-color: var(--slate-200);
background-color: color-mix(in srgb, var(--foreground) 20%, var(--background));
&:hover,
&:focus-visible {
color: var(--white);
color: var(--background);
background-color: var(--red-500);
}
}
@@ -76,7 +77,7 @@
}
&.btn-shadow {
box-shadow: color-mix(in srgb, var(--black) 10%, transparent) 0 .2rem .3rem;
box-shadow: var(--shadow);
}
}
@@ -85,41 +86,42 @@
place-items: center;
height: 45px;
aspect-ratio: 1;
background-color: var(--white);
border: 1px solid var(--slate-400);
box-shadow: color-mix(in srgb, var(--black) 10%, transparent) 0px 4px 12px;
background-color: var(--background);
border: 1px solid var(--border);
box-shadow: var(--shadow);
border-radius: 100vmax;
pointer-events: all;
cursor: pointer;
outline: .2rem solid transparent;
transition-property: color, background-color, border-color, outline-color;
transition-duration: .15s;
transition-timing-function: cubic-bezier(0.445, 0.05, 0.55, 0.95);
transition-timing-function: var(--timing-function);
svg {
fill: var(--foreground);
width: 1.15em;
transition-property: fill;
transition-duration: .15s;
transition-timing-function: cubic-bezier(0.445, 0.05, 0.55, 0.95);
transition-timing-function: var(--timing-function);
}
&:hover,
&:focus-visible {
outline-color: color-mix(in srgb, var(--blue-500) 20%, transparent);
border-color: var(--blue-500);
outline-color: color-mix(in srgb, var(--accent) 20%, transparent);
border-color: var(--accent);
svg {
fill: var(--blue-500);
fill: var(--accent);
}
}
&.active {
color: var(--white);
background-color: var(--blue-500);
border-color: var(--blue-700);
color: var(--background);
background-color: var(--accent);
border-color: color-mix(in srgb, var(--accent) 75%, var(--background));
svg {
fill: var(--white);
fill: var(--background);
}
}
}

View File

@@ -2,13 +2,14 @@
padding-block: 0.75rem;
padding-inline: .75rem;
border-radius: 8px;
background-color: var(--white);
border: 1px solid var(--slate-100);
box-shadow: color-mix(in srgb, var(--black) 10%, transparent) 0px 4px 12px;
color: var(--foreground);
background-color: var(--background);
border: 1px solid var(--border);
box-shadow: var(--shadow);
min-width: 15rem;
.card-arrow {
fill: var(--white);
fill: var(--background);
}
&.p-sm {

View File

@@ -14,15 +14,15 @@
display: block;
width: 100%;
padding: .4rem .5rem;
border: 1px solid var(--slate-300);
border: 1px solid color-mix(in srgb, var(--foreground) 30%, var(--background));
font-size: .8em;
border-radius: .2rem;
border-radius: var(--border-radius);
transition-property: box-shadow;
transition-duration: .2s;
transition-timing-function: cubic-bezier(0.785, 0.135, 0.15, 0.86);
transition-timing-function: var(--timing-function);
&:focus-visible {
box-shadow: 0 0 0 .2rem color-mix(in srgb, var(--slate-300) 20%, transparent);
box-shadow: 0 0 0 .2rem color-mix(in srgb, var(--accent) 20%, transparent);
}
}
}

View File

@@ -10,7 +10,9 @@
.leaflet-map-pane {
.leaflet-popup-pane {
.leaflet-popup-content-wrapper {
border-radius: .25rem;
color: var(--foreground);
background-color: var(--background);
border-radius: var(--border-radius);
overflow: clip;
.leaflet-popup-content {
@@ -22,6 +24,7 @@
.leaflet-popup-tip {
width: .8rem;
height: .8rem;
background-color: var(--background);
}
}
@@ -29,15 +32,18 @@
.leaflet-tooltip {
&.capital-name {
padding: .2rem .4rem;
color: #EEE;
color: var(--foreground);
font-weight: bold;
background: none;
border: none;
box-shadow: none;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
transition-property: color, text-shadow;
transition-duration: .15s;
transition-timing-function: var(--timing-function);
@include text.stroke(2, #18181b);
@include text.stroke(2, var(--background));
&::before {
display: none;
@@ -47,3 +53,56 @@
}
}
}
.leaflet-touch .leaflet-bar {
border-color: var(--border);
.leaflet-control-zoom-in,
.leaflet-control-zoom-out {
color: var(--foreground);
background-color: var(--background);
border-color: var(--border);
transition-property: color, background-color, border-color, outline-color;
transition-duration: .15s;
transition-timing-function: var(--timing-function);
&:hover,
&:focus-visible {
color: var(--background);
background-color: var(--accent);
}
}
}
.leaflet-touch .leaflet-control-measure {
color: var(--foreground);
background-color: var(--background);
transition-property: color, background-color, border-color, outline-color;
transition-duration: .15s;
transition-timing-function: var(--timing-function);
.leaflet-control-measure-toggle {
color: var(--foreground);
background-color: var(--background);
border-color: var(--border);
background-image: url('/icon/ruler.svg');
background-size: 66%;
transition-property: color, background-color, border-color, outline-color;
transition-duration: .15s;
transition-timing-function: var(--timing-function);
&:hover,
&:focus-visible {
color: var(--background);
background-color: var(--accent);
}
}
}
.dark {
.leaflet-touch .leaflet-control-measure {
.leaflet-control-measure-toggle {
background-image: url('/icon/ruler-dark.svg');
}
}
}

View File

@@ -1,8 +1,8 @@
.dialog-overlay {
background-color: color-mix(in srgb, var(--black) 40%, transparent);
background-color: color-mix(in srgb, var(--black) 50%, transparent);
position: fixed;
inset: 0;
animation: show-overlay 150ms cubic-bezier(0.16, 1, 0.3, 1);
animation: show-overlay 150ms var(--timing-function);
z-index: 100;
}
@@ -19,11 +19,12 @@
padding: 25px;
display: grid;
grid-template-rows: auto 1fr;
border-radius: 8px;
background-color: var(--white);
border: 1px solid var(--slate-100);
box-shadow: color-mix(in srgb, var(--black) 10%, transparent) 0px 4px 12px;
animation: show-content 150ms cubic-bezier(0.16, 1, 0.3, 1);
color: var(--foreground);
border-radius: var(--border-radius);
background-color: var(--background);
border: 1px solid var(--border);
box-shadow: var(--shadow);
animation: show-content 150ms var(--timing-function);
&:focus {
outline: none;
@@ -46,7 +47,7 @@
}
font-size: 1em;
color: var(--slate-800);
color: color-mix(in srgb, var(--foreground) 80%, var(--background));
line-height: 1.5;
> * + * {
@@ -63,11 +64,6 @@
text-decoration: none;
color: var(--green-500);
}
&::after {
top: 50%;
transform: translateY(-70%);
}
}
}

View File

@@ -23,16 +23,16 @@
grid-template-columns: auto max-content;
column-gap: 15px;
align-items: center;
color: var(--slate-800);
border: 1px solid var(--slate-400);
background-color: var(--white);
border-radius: 6px;
color: var(--foreground);
border: 1px solid var(--border);
background-color: var(--background);
border-radius: var(--border-radius);
box-shadow: hsl(206 22% 7% / 35%) 0px 10px 38px -10px, hsl(206 22% 7% / 20%) 0px 10px 20px -15px;
&.toast-success {
color: var(--green-700);
border: 1px solid var(--green-700);
background-color: color-mix(in srgb, var(--green-500) 17.5%, var(--white));
background-color: color-mix(in srgb, var(--green-500) 17.5%, var(--background));
.toast-icon svg {
fill: var(--green-700);
@@ -51,20 +51,20 @@
}
}
.toast-root[data-state='open'] {
animation: slideIn 150ms cubic-bezier(0.16, 1, 0.3, 1);
animation: slideIn 150ms var(--timing-function);
}
.toast-root[data-state='closed'] {
animation: hide 100ms ease-in;
animation: hide 100ms var(--timing-function);
}
.toast-root[data-swipe='move'] {
transform: translateX(var(--radix-toast-swipe-move-x));
}
.toast-root[data-swipe='cancel'] {
transform: translateX(0);
transition: transform 200ms ease-out;
transition: transform 200ms var(--timing-function);
}
.toast-root[data-swipe='end'] {
animation: swipeOut 100ms ease-out;
animation: swipeOut 100ms var(--timing-function);
}
@keyframes hide {

View File

@@ -37,19 +37,34 @@
--orange-700: #c2410c;
--purple-500: #8b5cf6;
--purple-700: #6d28d9;
--foreground: var(--black);
--background: var(--white);
--border: var(--slate-400);
--accent: var(--blue-500);
--border-radius: .25rem;
--shadow: color-mix(in srgb, var(--foreground) 10%, transparent) 0px 4px 12px;
--timing-function: cubic-bezier(0.785, 0.135, 0.15, 0.86);
}
.dark {
--foreground: var(--slate-300);
--background: var(--slate-950);
--border: var(--slate-600);
}
html {
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
color: var(--slate-900);
color: var(--foreground);
}
a[href^='http'] {
padding-right: 1.25em;
white-space: nowrap;
orphans: 2;
}
a[href^='http']::after {
position: absolute;
content: url('/icon/square-arrow-out.svg');
display: inline-block;
width: 1em;
@@ -57,6 +72,12 @@ a[href^='http']::after {
margin-left: .25em;
}
.dark {
a[href^='http']::after {
content: url('/icon/square-arrow-out-dark.svg');
}
}
strong {
font-weight: 600;
}

View File

@@ -62,7 +62,7 @@ button {
display: inline-flex;
align-items: center;
gap: .5ch;
color: var(--slate-700);
color: color-mix(in srgb, var(--foreground) 70%, var(--background));
cursor: pointer;
text-underline-offset: 2px;
text-decoration: none;
@@ -75,7 +75,7 @@ button {
svg {
width: 1rem;
height: 1rem;
fill: var(--slate-700);
fill: color-mix(in srgb, var(--foreground) 70%, var(--background));
}
}
</style>

View File

@@ -0,0 +1,32 @@
<script lang="ts" setup>
import { useDark, useToggle } from '@vueuse/core'
import { currentLang, t } from '@/i18n/store';
const isDark = useDark({ storageKey: "theme", disableTransition: false })
const toggleDark = useToggle(isDark)
</script>
<template>
<div data-theme-toggle id="theme-switcher" class="wrapper appear-from-top">
<button @click="toggleDark()" class="btn-round" :title="t('nav.theme.aria-label')">
<svg v-if="isDark" xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M120,40V32a8,8,0,0,1,16,0v8a8,8,0,0,1-16,0Zm72,88a64,64,0,1,1-64-64A64.07,64.07,0,0,1,192,128Zm-16,0a48,48,0,1,0-48,48A48.05,48.05,0,0,0,176,128ZM58.34,69.66A8,8,0,0,0,69.66,58.34l-8-8A8,8,0,0,0,50.34,61.66Zm0,116.68-8,8a8,8,0,0,0,11.32,11.32l8-8a8,8,0,0,0-11.32-11.32ZM192,72a8,8,0,0,0,5.66-2.34l8-8a8,8,0,0,0-11.32-11.32l-8,8A8,8,0,0,0,192,72Zm5.66,114.34a8,8,0,0,0-11.32,11.32l8,8a8,8,0,0,0,11.32-11.32ZM40,120H32a8,8,0,0,0,0,16h8a8,8,0,0,0,0-16Zm88,88a8,8,0,0,0-8,8v8a8,8,0,0,0,16,0v-8A8,8,0,0,0,128,208Zm96-88h-8a8,8,0,0,0,0,16h8a8,8,0,0,0,0-16Z"></path></svg>
<svg v-else xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M233.54,142.23a8,8,0,0,0-8-2,88.08,88.08,0,0,1-109.8-109.8,8,8,0,0,0-10-10,104.84,104.84,0,0,0-52.91,37A104,104,0,0,0,136,224a103.09,103.09,0,0,0,62.52-20.88,104.84,104.84,0,0,0,37-52.91A8,8,0,0,0,233.54,142.23ZM188.9,190.34A88,88,0,0,1,65.66,67.11a89,89,0,0,1,31.4-26A106,106,0,0,0,96,56,104.11,104.11,0,0,0,200,160a106,106,0,0,0,14.92-1.06A89,89,0,0,1,188.9,190.34Z"></path></svg>
</button>
</div>
</template>
<style lang="scss" scoped>
.wrapper {
pointer-events: all;
}
.btn-round {
position: relative;
> svg {
width: 1.25em;
height: 1.25em;
}
}
</style>

View File

@@ -8,6 +8,7 @@ import SearchMarkers from "./overlay/SearchMarkers.vue";
import LangSwitcher from "./overlay/LangSwitcher.vue";
import MapOverlayBreadcrumbs from "./MapOverlayBreadcrumbs.astro";
import ToastService from "./overlay/ToastService.vue";
import ThemeSwitch from "../global/ThemeSwitch.vue";
interface Props extends MapOverlayProps {}
@@ -41,9 +42,14 @@ const currentUrl = Astro.url
</SearchMarkers>
}
{ENABLE_I18N &&
<LangSwitcher currentUrl={currentUrl} client:only="vue" />
}
<div class="right-menu">
{ENABLE_I18N &&
<LangSwitcher currentUrl={currentUrl} client:only="vue" />
}
<ThemeSwitch client:only="vue" />
</div>
</div>
{ENABLE_BREADCRUMB &&
@@ -71,5 +77,15 @@ const currentUrl = Astro.url
align-items: top;
gap: 1rem;
}
.right-menu {
display: grid;
gap: .5rem;
align-content: flex-start;
@media screen and (width >= 900px) {
display: flex;
}
}
}
</style>

View File

@@ -14,7 +14,7 @@ const { breadcrumbs = [] } = Astro.props
{breadcrumbs.map((breadcrumb, index) => (
<li>
{index === breadcrumbs.length - 1 &&
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#0f172a" viewBox="0 0 256 256" class="folder"><path d="M245,110.64A16,16,0,0,0,232,104H216V88a16,16,0,0,0-16-16H130.67L102.94,51.2a16.14,16.14,0,0,0-9.6-3.2H40A16,16,0,0,0,24,64V208h0a8,8,0,0,0,8,8H211.1a8,8,0,0,0,7.59-5.47l28.49-85.47A16.05,16.05,0,0,0,245,110.64ZM93.34,64,123.2,86.4A8,8,0,0,0,128,88h72v16H69.77a16,16,0,0,0-15.18,10.94L40,158.7V64Z"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#0f172a" viewBox="0 0 256 256" class="icon folder"><path d="M245,110.64A16,16,0,0,0,232,104H216V88a16,16,0,0,0-16-16H130.67L102.94,51.2a16.14,16.14,0,0,0-9.6-3.2H40A16,16,0,0,0,24,64V208h0a8,8,0,0,0,8,8H211.1a8,8,0,0,0,7.59-5.47l28.49-85.47A16.05,16.05,0,0,0,245,110.64ZM93.34,64,123.2,86.4A8,8,0,0,0,128,88h72v16H69.77a16,16,0,0,0-15.18,10.94L40,158.7V64Z"></path></svg>
<span class="breadcrumb" aria-current="page">
<h1 class="label">
@@ -23,7 +23,7 @@ const { breadcrumbs = [] } = Astro.props
</span>
}
{!(index === breadcrumbs.length - 1) &&
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#0f172a" viewBox="0 0 256 256" class="folder"><path d="M216,72H131.31L104,44.69A15.86,15.86,0,0,0,92.69,40H40A16,16,0,0,0,24,56V200.62A15.4,15.4,0,0,0,39.38,216H216.89A15.13,15.13,0,0,0,232,200.89V88A16,16,0,0,0,216,72ZM40,56H92.69l16,16H40ZM216,200H40V88H216Z"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#0f172a" viewBox="0 0 256 256" class="icon folder"><path d="M216,72H131.31L104,44.69A15.86,15.86,0,0,0,92.69,40H40A16,16,0,0,0,24,56V200.62A15.4,15.4,0,0,0,39.38,216H216.89A15.13,15.13,0,0,0,232,200.89V88A16,16,0,0,0,216,72ZM40,56H92.69l16,16H40ZM216,200H40V88H216Z"></path></svg>
<a href={breadcrumb.url} class="breadcrumb" class:list={{ active: index === breadcrumbs.length - 1 }} data-astro-prefetch="hover">
<span class="label">
@@ -31,7 +31,7 @@ const { breadcrumbs = [] } = Astro.props
</span>
</a>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="#0f172a" viewBox="0 0 256 256"><path d="M181.66,133.66l-80,80a8,8,0,0,1-11.32-11.32L164.69,128,90.34,53.66a8,8,0,0,1,11.32-11.32l80,80A8,8,0,0,1,181.66,133.66Z"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="#0f172a" viewBox="0 0 256 256" class="icon"><path d="M181.66,133.66l-80,80a8,8,0,0,1-11.32-11.32L164.69,128,90.34,53.66a8,8,0,0,1,11.32-11.32l80,80A8,8,0,0,1,181.66,133.66Z"></path></svg>
}
</li>
))}
@@ -48,17 +48,25 @@ menu {
margin-top: .5rem;
padding-block: .25rem;
padding-inline: .8rem;
border: 1px solid var(--slate-400);
background: var(--white);
color: var(--foreground);
border: 1px solid var(--border);
background: var(--background);
border-radius: 100vmax;
box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;
pointer-events: all;
transition-property: color, background-color, border-color, outline-color;
transition-duration: .15s;
transition-timing-function: var(--timing-function);
li {
display: flex;
align-items: center;
gap: .25ch;
svg {
fill: var(--foreground);
}
&:has(a:hover, a:focus-visible) {
svg.folder {
fill: var(--blue-500);
@@ -83,7 +91,7 @@ menu {
}
.icon {
width: 1.1em;
fill: var(--foreground);
}
.label {

View File

@@ -49,7 +49,7 @@ a {
&:hover,
&:focus-visible {
background-color: var(--slate-200);
background-color: color-mix(in srgb, var(--foreground) 20%, var(--background));
}
&[aria-current="page"] {

View File

@@ -275,10 +275,11 @@ menu {
padding-block: .25rem;
top: calc(var(--mouse-y) - .5rem);
left: calc(var(--mouse-x) + .75rem);
background-color: var(--white);
border: 1px solid var(--slate-400);
color: var(--foreground);
background-color: var(--background);
border: 1px solid var(--border);
border-radius: .3rem;
box-shadow: rgba(0, 0, 0, 0.3) 0px 4px 12px;
box-shadow: var(--shadow);
z-index: 1000;
transition-property: top, left;
transition-duration: .16s;
@@ -295,13 +296,17 @@ menu {
cursor: pointer;
width: 100%;
svg {
fill: var(--foreground);
}
&:hover,
&:focus-within {
color: var(--blue-700);
background-color: var(--slate-100);
color: color-mix(in srgb, var(--accent) 90%, var(--background));
background-color: color-mix(in srgb, var(--accent) 10%, var(--background));
svg {
fill: var(--blue-700);
fill: color-mix(in srgb, var(--accent) 75%, var(--background));
}
}
}
@@ -313,10 +318,11 @@ dialog {
max-width: calc(100% - 6rem);
padding: 1.5rem 2rem;
z-index: 9999;
background-color: var(--white);
border: 1px solid var(--slate-400);
color: var(--foreground);
background-color: var(--background);
border: 1px solid var(--border);
border-radius: .75rem;
box-shadow: rgba(0, 0, 0, 0.3) 0px 4px 12px;
box-shadow: var(--shadow);
overflow: visible;
transform: translateY(1rem);
transition: visibility 0s ease-out .3s, opacity .3s ease-out, transform .3s ease-out;

View File

@@ -94,8 +94,7 @@ const menus: Menu[] = [
{
name: 'Mines Blanches',
img: '/images/aldys-cantane-mines-blanches-cover.png',
url: `/${$currentLang.value}/aldys/cantane/mines-blanches`,
newItem: true
url: `/${$currentLang.value}/aldys/cantane/mines-blanches`
},
]
}
@@ -213,6 +212,10 @@ const menus: Menu[] = [
}
}
.dark .legal::before {
content: url('/icon/question-mark-dark.svg');
}
.map-menu {
margin-top: .25rem;
margin-bottom: .5rem;
@@ -251,7 +254,7 @@ const menus: Menu[] = [
img {
display: block;
border-radius: .3rem;
border: .1rem solid var(--white);
border: .1rem solid var(--background);
outline: .15rem solid transparent;
}
@@ -264,15 +267,15 @@ const menus: Menu[] = [
pointer-events: none;
.fill { fill: var(--orange-500); }
.stroke { fill: var(--white); }
.stroke { fill: var(--background); }
}
}
figcaption {
font-size: .75em;
text-align: center;
color: var(--slate-500);
color: color-mix(in srgb, var(--foreground) 80%, var(--background));
}
}
}
</style>
</style>

View File

@@ -308,11 +308,15 @@ function resetAllFields(actionAfter?: "focusAfter") {
margin-left: calc(45px + .5rem);
padding: .5rem 1.2rem;
width: calc(100% - 3rem);
background: var(--white);
border: 1px solid var(--slate-400);
color: var(--foreground);
background: var(--background);
border: 1px solid var(--border);
border-radius: 25px;
box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;
box-shadow: var(--shadow);
pointer-events: all;
transition-property: color, background-color, border-color, outline-color;
transition-duration: .15s;
transition-timing-function: var(--timing-function);
@media screen and (width >= 900px) {
width: clamp(25rem, 35dvw, 30rem);
@@ -338,7 +342,7 @@ function resetAllFields(actionAfter?: "focusAfter") {
flex-grow: 1;
&::placeholder {
color: var(--slate-400);
color: color-mix(in srgb, var(--foreground) 75%, var(--background));
}
}
@@ -348,7 +352,7 @@ function resetAllFields(actionAfter?: "focusAfter") {
transform: translateY(-50%);
left: 0;
width: $search-icon-size;
fill: var(--slate-400);
fill: color-mix(in srgb, var(--foreground) 75%, var(--background));
pointer-events: none;
}
@@ -375,16 +379,20 @@ function resetAllFields(actionAfter?: "focusAfter") {
}
&:hover {
.icon {
color: var(--foreground);
}
&::before {
background-color: var(--slate-300);
background-color: color-mix(in srgb, var(--foreground) 20%, var(--background));
}
}
&:focus-visible {
&::before {
background-color: var(--slate-200);
outline: 1px dotted var(--slate-500);
outline: 4px auto var(--slate-900);
background-color: color-mix(in srgb, var(--foreground) 20%, var(--background));
outline: 1px dotted color-mix(in srgb, var(--foreground) 50%, var(--background));
outline: 4px auto color-mix(in srgb, var(--foreground) 90%, var(--background));
}
}
}
@@ -399,22 +407,22 @@ function resetAllFields(actionAfter?: "focusAfter") {
&:hover {
&::before {
background-color: var(--slate-300);
background-color: color-mix(in srgb, var(--foreground) 20%, var(--background));
}
}
&:focus-visible {
&::before {
background-color: var(--slate-200);
outline: 1px dotted var(--slate-500);
outline: 4px auto var(--slate-900);
background-color: color-mix(in srgb, var(--foreground) 20%, var(--background));
outline: 1px dotted color-mix(in srgb, var(--foreground) 50%, var(--background));
outline: 4px auto color-mix(in srgb, var(--foreground) 90%, var(--background));
}
}
}
.close-btn {
font-size: 1.1em;
color: var(--slate-500);
color: color-mix(in srgb, var(--foreground) 75%, var(--background));
}
}
@@ -436,7 +444,7 @@ function resetAllFields(actionAfter?: "focusAfter") {
}
&::-webkit-scrollbar-thumb {
background-color: var(--slate-300);
background-color: color-mix(in srgb, var(--foreground) 30%, var(--background));
}
> * + * {
@@ -453,7 +461,7 @@ function resetAllFields(actionAfter?: "focusAfter") {
}
&:hover:not(:has(a:hover)) {
background-color: var(--slate-100);
background-color: color-mix(in srgb, var(--foreground) 10%, var(--background));
.title {
text-decoration: underline;
@@ -490,9 +498,14 @@ function resetAllFields(actionAfter?: "focusAfter") {
.title {
font-weight: 500;
text-underline-offset: 2px;
& + .desc {
margin-top: .25rem;
}
}
.desc {
color: var(--slate-500);
color: color-mix(in srgb, var(--foreground) 50%, var(--background));
.map-link {
margin-top: .5rem;
@@ -528,12 +541,12 @@ function resetAllFields(actionAfter?: "focusAfter") {
top: 50%;
transform: translateY(-50%);
right: .75rem;
fill: var(--slate-400);
fill: color-mix(in srgb, var(--foreground) 70%, var(--background));
}
&:focus-visible {
outline: 1px dotted var(--slate-500);
outline: 4px auto var(--slate-900);
outline: 1px dotted color-mix(in srgb, var(--foreground) 50%, var(--background));
outline: 4px auto color-mix(in srgb, var(--foreground) 90%, var(--background));
.title {
text-decoration: underline;
@@ -600,8 +613,9 @@ function resetAllFields(actionAfter?: "focusAfter") {
&[data-focused="true"] {
margin-bottom: 1em;
.input-w {
border-bottom: 1px solid var(--slate-200);
border-bottom: 1px solid color-mix(in srgb, var(--foreground) 20%, var(--background));
margin-bottom: .5rem;
padding-bottom: .5rem;

View File

@@ -125,10 +125,11 @@ function emitCategorySwitch(newCategory: MapMarkerGroup) {
padding-inline: .8rem;
font-weight: 600;
font-size: .85em;
background: var(--white);
border: 1px solid var(--slate-400);
color: var(--foreground);
background: var(--background);
border: 1px solid var(--border);
border-radius: 100vmax;
box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;
box-shadow: var(--shadow);
white-space: nowrap;
pointer-events: all;
cursor: pointer;
@@ -145,6 +146,10 @@ function emitCategorySwitch(newCategory: MapMarkerGroup) {
width: 1.1em;
}
svg {
fill: var(--foreground);
}
.label {
text-underline-offset: .15rem;
}
@@ -154,12 +159,12 @@ function emitCategorySwitch(newCategory: MapMarkerGroup) {
@each $c in $colors {
&.#{$c} {
&.active {
color: var(--white);
color: var(--background);
background-color: var(--#{$c}-500);
border-color: var(--#{$c}-700);
.icon {
fill: var(--white);
fill: var(--background);
}
}

View File

@@ -21,9 +21,10 @@ export const translations: LanguageDict = {
'capitals': 'Capitales',
'points-of-interest': 'Points d\'intérêt',
'personals': 'Personnels',
'nav.aria-label': 'Navigation principale',
'nav.aria-label': 'Menu principal',
'nav.map.aria-label': 'Changer de carte',
'nav.lang.aria-label': 'Changer de langue',
'nav.theme.aria-label': 'Changer de thème',
'maps.go-to-map': 'Voir la carte détaillée',
'maps.go-to-player': 'Aller à la position actuelle des joueurs',
'maps.seeQuest': 'Voir la quête',
@@ -65,9 +66,10 @@ export const translations: LanguageDict = {
'capitals': 'Capitals',
'points-of-interest': 'Points of interest',
'personals': 'Personals',
'nav.aria-label': 'Main navigation',
'nav.aria-label': 'Main menu',
'nav.map.aria-label': 'Switch map',
'nav.lang.aria-label': 'Switch language',
'nav.theme.aria-label': 'Switch color theme',
'maps.go-to-map': 'See detailed map',
'maps.go-to-player': 'Go to current players position',
'maps.seeQuest': 'See the quest',

View File

@@ -16,6 +16,13 @@ const lang = getLangFromUrl(Astro.url) || defaultLang;
<!doctype html>
<html lang={lang}>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{title} — Leim Wiki</title>
<script is:inline src="/leaflet/leaflet.js" />
<script is:inline type="text/javascript" src="/js/leaflet-measure.min.js"></script>
<script is:inline type="text/javascript" src="/js/leaflet-zoomify.min.js"></script>
<script>
import { setLang } from "@/i18n/store";
import { defaultLang, type Language } from "@/i18n/ui";
@@ -25,12 +32,21 @@ const lang = getLangFromUrl(Astro.url) || defaultLang;
setLang(currentLang || defaultLang);
</script>
<meta charset="UTF-8" />
<link rel="stylesheet" href="/leaflet/leaflet.css" rel="preload" />
<link rel="stylesheet" href="/leaflet-measure/leaflet-measure.css" rel="preload" />
<meta name="author" content="Alexis Pelé" />
{head?.description && <meta name="description" content={head.description} /> }
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta property="og:author" content="Alexis Pelé" />
<meta property="og:locale" content={lang}>
<meta name="application-name" content="leim-maps">
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-config" content="none">
{head?.description && (
<meta name="description" content={head.description} />
<meta property="og:description" content={head.description} />
)}
<meta name="generator" content={Astro.generator} />
<title>{title} — Leim Wiki</title>
<link rel="canonical" href={Astro.url}>
@@ -41,24 +57,68 @@ const lang = getLangFromUrl(Astro.url) || defaultLang;
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#6595b4">
<meta name="msapplication-TileColor" content="#00aba9">
<meta name="theme-color" content="#a9d1eb">
<meta name="color-scheme" content="dark light">
<meta property="og:title" content={title} />
<meta property="og:type" content="website" />
<meta property="og:url" content={Astro.url} />
{head?.image && <meta property="og:image" content={head.image} />}
{head?.description && <meta name="twitter:description" content={head.description} /> }
{head?.image && <meta property="twitter:image" content={head.image} />}
<link rel="stylesheet" href="/leaflet/leaflet.css" rel="preload" />
<script is:inline src="/leaflet/leaflet.js" />
<script is:inline type="text/javascript" src="/js/leaflet-zoomify.min.js"></script>
<link rel="stylesheet" href="/leaflet-measure/leaflet-measure.css" rel="preload" />
<script is:inline type="text/javascript" src="/js/leaflet-measure.min.js"></script>
{head?.image && (
<meta property="og:image" content={`${Astro.site?.origin}${head.image}`} />
<meta name="twitter:image" content={`${Astro.site?.origin}${head.image}`} />
<meta name="twitter:card" content="summary_large_image" />
)}
</head>
<body>
<script is:inline>
let initialTheme = localStorage.getItem("theme");
const darkModeMq = window.matchMedia("(prefers-color-scheme: dark)");
// Initial paint from localstorage
switch (initialTheme) {
case "light":
enableLightTheme();
break;
case "system":
resetTheme();
break;
case "dark":
default:
enableDarkTheme();
break;
}
// Enables dark theme and updates preferences
function enableDarkTheme() {
document.documentElement.classList.add('dark');
document.documentElement.classList.remove('light');
localStorage.setItem("theme", "dark");
}
// Enables light theme and updates preferences
function enableLightTheme() {
document.documentElement.classList.add('light');
document.documentElement.classList.remove('dark');
localStorage.setItem("theme", "light");
}
// Reset theme to OS values
function resetTheme() {
const prefersDark = darkModeMq.matches;
if (prefersDark) {
document.documentElement.classList.add('dark');
document.documentElement.classList.remove('light');
} else {
document.documentElement.classList.add('light');
document.documentElement.classList.remove('dark');
}
localStorage.setItem("theme", "system");
}
</script>
<slot />
</body>
</html>

View File

@@ -4,7 +4,12 @@ import Map from '@/components/maps/Map.astro'
import MapOverlay from '@/components/maps/MapOverlay.astro'
---
<Layout title="Borélis ~ Maps">
<Layout
title="Borélis ~ Maps"
head={{
description: "Map of Borélis, a harbour city on the northern shores of the Kaldelium Accord."
}}
>
<MapOverlay
mapKey='aldys-borelis'
searchConfig={{

View File

@@ -1,5 +1,5 @@
---
import type { MapMarker, PlayerMarker } from '@/types/Leaflet';
import type { MapMarker } from '@/types/Leaflet';
import Layout from '@/layouts/Layout.astro';
import Map from '@/components/maps/Map.astro'
@@ -10,7 +10,12 @@ import markersData from '@/components/maps/data/en/aldys/cantane/markers.json'
const markers = markersData.data as MapMarker[]
---
<Layout title="Cantane ~ Maps">
<Layout
title="Cantane ~ Maps"
head={{
description: "Map of the old city of Cantane, south of the Kaldelium Accord."
}}
>
<MapOverlay
mapKey='aldys-cantane'
markers={markers}

View File

@@ -10,7 +10,12 @@ import markersData from '@/components/maps/data/en/aldys/cantane/les-mines-blanc
const markers = markersData.data as MapMarker[]
---
<Layout title="White Hollow ~ Maps">
<Layout
title="White Hollow ~ Maps"
head={{
description: "Map of the White Hollow mining complex, left abandonned east of Cantane."
}}
>
<MapOverlay
mapKey='aldys-cantane-mines'
markers={markers}

View File

@@ -12,7 +12,12 @@ const markers = markersData.data as MapMarker[]
const players = playersData as PlayerMarker
---
<Layout title="Bamast ~ Maps">
<Layout
title="Bamast ~ Maps"
head={{
description: "Map of the archipelago of Bamast and its many islands in the Balamian Ocean."
}}
>
<MapOverlay
mapKey='bamast'
markers={markers}

View File

@@ -16,8 +16,8 @@ const players = playersData as PlayerMarker
title="Aldys ~ Maps"
head={
{
description: "Carte de l'Alliance Kaldélienne sur la planète de Léim.",
image: '/images/bailey-zindel-NRQV-hBF10M-unsplash.jpg'
description: "Map of the Aldys continent on the planet of Léim, displaying the Kaldelium accord and the Common Lands of Syaltha.",
image: '/bailey-zindel-NRQV-hBF10M-unsplash.jpg'
}
}
>

View File

@@ -4,7 +4,12 @@ import Map from '@/components/maps/Map.astro'
import MapOverlay from '@/components/maps/MapOverlay.astro'
---
<Layout title="Borélis ~ Cartographie">
<Layout
title="Borélis ~ Cartographie"
head={{
description: "Carte de la ville côtière de Borélis, sur les côtes gelées au nord de l'Alliance Kaldélienne."
}}
>
<MapOverlay
mapKey='aldys-borelis'
searchConfig={{

View File

@@ -10,7 +10,12 @@ import markersData from '@/components/maps/data/fr/aldys/cantane/markers.json'
const markers = markersData.data as MapMarker[]
---
<Layout title="Cantane ~ Cartographie">
<Layout
title="Cantane ~ Cartographie"
head={{
description: "Carte de la ville de Cantane, au sud de l'Alliance Kaldélienne."
}}
>
<MapOverlay
mapKey='aldys-cantane'
markers={markers}

View File

@@ -10,7 +10,12 @@ import markersData from '@/components/maps/data/fr/aldys/cantane/les-mines-blanc
const markers = markersData.data as MapMarker[]
---
<Layout title="Les Mines Blanches ~ Cartographie">
<Layout
title="Les Mines Blanches ~ Cartographie"
head={{
description: "Cartographie du complexe des Mines Blanches, laissé à l'abandon à l'est de Cantane."
}}
>
<MapOverlay
mapKey='aldys-cantane-mines'
markers={markers}

View File

@@ -12,7 +12,12 @@ const markers = markersData.data as MapMarker[]
const players = playersData as PlayerMarker
---
<Layout title="Bamast ~ Cartographie">
<Layout
title="Bamast ~ Cartographie"
head={{
description: "Carte de l'Archipel de Bamast et ses nombreuses îles de l'Océan Balamien."
}}
>
<MapOverlay
mapKey='bamast'
markers={markers}

View File

@@ -16,8 +16,8 @@ const players = playersData as PlayerMarker
title="Aldys ~ Cartographie"
head={
{
description: "Carte de l'Alliance Kaldélienne sur la planète de Léim.",
image: '/images/bailey-zindel-NRQV-hBF10M-unsplash.jpg'
description: "Carte du continent Aldys sur la planète de Léim, comprenant l'Alliance Kaldélienne et les Terres de Syaltha.",
image: '/bailey-zindel-NRQV-hBF10M-unsplash.jpg'
}
}
>