generated from Seekra/repository-template
Add option to adapt color scheme to the system color scheme #61
+7
-3
@@ -17,14 +17,18 @@ limitations under the License.
|
|||||||
<script setup>
|
<script setup>
|
||||||
import Navbar from './features/nav/components/Navbar.vue';
|
import Navbar from './features/nav/components/Navbar.vue';
|
||||||
import Footer from './features/footer/components/Footer.vue';
|
import Footer from './features/footer/components/Footer.vue';
|
||||||
|
|
||||||
|
import { useColorTheme } from './features/colorTheme/composables/useColorTheme';
|
||||||
import { ref, provide, watch } from 'vue';
|
import { ref, provide, watch } from 'vue';
|
||||||
|
|
||||||
const isDark = ref(localStorage.getItem('theme') === 'dark');
|
const { getColorTheme, updateColorTheme, appHasDarkClass } = useColorTheme();
|
||||||
provide('isDark', isDark);
|
const colorTheme = ref(getColorTheme());
|
||||||
|
provide('colorTheme', colorTheme);
|
||||||
|
watch(colorTheme, val => updateColorTheme(val))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="{ dark: isDark }" class="app-wrapper">
|
<div :class="{ dark: appHasDarkClass(colorTheme) }" class="app-wrapper">
|
||||||
<Navbar />
|
<Navbar />
|
||||||
|
|
||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
<!--
|
||||||
|
Copyright 2026 Seekra
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { inject } from 'vue';
|
||||||
|
|
||||||
|
const colorTheme = inject('colorTheme');
|
||||||
|
|
||||||
|
const colorThemeNextMapper = {
|
||||||
|
'light': 'dark',
|
||||||
|
'dark': 'light'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<button class="color-theme-button"
|
||||||
|
@click="colorTheme = colorThemeNextMapper[colorTheme]"
|
||||||
|
:aria-label="`Switch to ${colorThemeNextMapper[colorTheme]} mode`"
|
||||||
|
:title="`Switch to ${colorThemeNextMapper[colorTheme]} mode`"
|
||||||
|
>
|
||||||
|
<span v-if="colorTheme === 'dark'">☀</span>
|
||||||
|
<span v-else-if="colorTheme === 'light'">⏾</span>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.color-theme-button {
|
||||||
|
background: none;
|
||||||
|
border: 1.5px solid var(--light-d-3);
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-theme-button:hover {
|
||||||
|
background: var(--light-d-2);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
<!--
|
|
||||||
Copyright 2026 Seekra
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { inject, watch } from 'vue';
|
|
||||||
|
|
||||||
const isDark = inject('isDark');
|
|
||||||
watch(isDark, val => localStorage.setItem('theme', val ? 'dark' : 'light'));
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<button class="dark-mode-toggle"
|
|
||||||
@click="isDark = !isDark"
|
|
||||||
:aria-label="isDark ? 'Light mode' : 'Dark mode'"
|
|
||||||
>
|
|
||||||
<span v-if="isDark">☀</span>
|
|
||||||
<span v-else>⏾</span>
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2026 Seekra
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const useColorTheme = function () {
|
||||||
|
const getColorTheme = function () {
|
||||||
|
let colorTheme = localStorage.getItem('theme') || 'light';
|
||||||
|
if (!(colorTheme === 'dark' || colorTheme === 'light')) {
|
||||||
|
colorTheme = 'light';
|
||||||
|
};
|
||||||
|
return colorTheme;
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateColorTheme = function (newTheme) {
|
||||||
|
let actualNewTheme = newTheme;
|
||||||
|
if (!(actualNewTheme === 'light' || actualNewTheme === 'dark')) {
|
||||||
|
actualNewTheme = 'light';
|
||||||
|
};
|
||||||
|
|
||||||
|
localStorage.setItem('theme', actualNewTheme);
|
||||||
|
};
|
||||||
|
|
||||||
|
const appHasDarkClass = function (colorTheme) {
|
||||||
|
return colorTheme === 'dark';
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
getColorTheme,
|
||||||
|
updateColorTheme,
|
||||||
|
appHasDarkClass
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -15,12 +15,9 @@ limitations under the License.
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import DarkModeToggleButton from '@/features/colorTheme/components/DarkModeToggleButton.vue';
|
import ColorThemeButton from '@/features/colorTheme/components/ColorThemeButton.vue';
|
||||||
|
|
||||||
import { inject } from 'vue';
|
|
||||||
import logo from '@/assets/logo.svg';
|
import logo from '@/assets/logo.svg';
|
||||||
|
|
||||||
const isDark = inject('isDark');
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<nav class="global-nav">
|
<nav class="global-nav">
|
||||||
@@ -29,7 +26,7 @@ const isDark = inject('isDark');
|
|||||||
</RouterLink>
|
</RouterLink>
|
||||||
<ul class="right-links">
|
<ul class="right-links">
|
||||||
<li>
|
<li>
|
||||||
<DarkModeToggleButton />
|
<ColorThemeButton />
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
@@ -59,22 +56,7 @@ const isDark = inject('isDark');
|
|||||||
.global-nav .right-links a:hover{
|
.global-nav .right-links a:hover{
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
.dark-mode-toggle {
|
|
||||||
background: none;
|
|
||||||
border: 1.5px solid var(--light-d-3);
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 1rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark-mode-toggle:hover {
|
|
||||||
background: var(--light-d-2);
|
|
||||||
}
|
|
||||||
.nav-logo {
|
.nav-logo {
|
||||||
height: 24px;
|
height: 24px;
|
||||||
width: auto;
|
width: auto;
|
||||||
|
|||||||
Reference in New Issue
Block a user