Add dark mode
This commit is contained in:
parent
a5562e7fa4
commit
7ab85991db
6 changed files with 113 additions and 0 deletions
|
@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Define CSS variables for color theme.
|
- Define CSS variables for color theme.
|
||||||
|
- Added dark mode.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|
32
src/App.vue
32
src/App.vue
|
@ -59,6 +59,38 @@ watch($$(currentUser), () => {
|
||||||
--shadow-color: #{$shadow-color};
|
--shadow-color: #{$shadow-color};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] {
|
||||||
|
--background-color: #{$dark-background-color};
|
||||||
|
--text-color: #{$dark-text-color};
|
||||||
|
--text-colorizer: #{$dark-text-colorizer};
|
||||||
|
--secondary-text-color: #{$dark-secondary-text-color};
|
||||||
|
--secondary-text-colorizer: #{$dark-secondary-text-colorizer};
|
||||||
|
--secondary-text-hover-color: #{$dark-secondary-text-hover-color};
|
||||||
|
--secondary-text-hover-colorizer: #{$dark-secondary-text-hover-colorizer};
|
||||||
|
--link-color: #{$dark-link-color};
|
||||||
|
--link-colorizer: #{$dark-link-colorizer};
|
||||||
|
--link-hover-color: #{$dark-link-hover-color};
|
||||||
|
--link-hover-colorizer: #{$dark-link-hover-colorizer};
|
||||||
|
--btn-background-color: #{$dark-btn-background-color};
|
||||||
|
--btn-background-hover-color: #{$dark-btn-background-hover-color};
|
||||||
|
--btn-text-color: #{$dark-btn-text-color};
|
||||||
|
--btn-text-colorizer: #{$dark-btn-text-colorizer};
|
||||||
|
--btn-text-hover-color: #{$dark-btn-text-hover-color};
|
||||||
|
--btn-text-hover-colorizer: #{$dark-btn-text-hover-colorizer};
|
||||||
|
--btn-secondary-background-color: #{$dark-btn-secondary-background-color};
|
||||||
|
--btn-secondary-text-color: #{$dark-btn-secondary-text-color};
|
||||||
|
--btn-disabled-background-color: #{$dark-btn-disabled-background-color};
|
||||||
|
--btn-disabled-text-color: #{$dark-btn-disabled-text-color};
|
||||||
|
--block-background-color: #{$dark-block-background-color};
|
||||||
|
--block-link-color: #{$dark-block-link-color};
|
||||||
|
--block-link-hover-color: #{$dark-block-link-hover-color};
|
||||||
|
--separator-color: #{$dark-separator-color};
|
||||||
|
--widget-background-color: #{$dark-widget-background-color};
|
||||||
|
--highlight-color: #{$dark-highlight-color};
|
||||||
|
--loader-color: #{$dark-loader-color};
|
||||||
|
--shadow-color: #{$dark-shadow-color};
|
||||||
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
background: var(--background-color);
|
background: var(--background-color);
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
|
|
|
@ -44,6 +44,7 @@ import { $, $computed } from "vue/macros"
|
||||||
import { useRouter } from "vue-router"
|
import { useRouter } from "vue-router"
|
||||||
|
|
||||||
import { Permissions } from "@/api/users"
|
import { Permissions } from "@/api/users"
|
||||||
|
import { useTheme } from "@/composables/theme"
|
||||||
import { useNotifications } from "@/store/notifications"
|
import { useNotifications } from "@/store/notifications"
|
||||||
import { useCurrentUser } from "@/store/user"
|
import { useCurrentUser } from "@/store/user"
|
||||||
import { useInstanceInfo } from "@/store/instance"
|
import { useInstanceInfo } from "@/store/instance"
|
||||||
|
@ -56,9 +57,11 @@ const {
|
||||||
} = $(useCurrentUser())
|
} = $(useCurrentUser())
|
||||||
const { instance } = $(useInstanceInfo())
|
const { instance } = $(useInstanceInfo())
|
||||||
const { loadNotifications, getUnreadNotificationCount } = $(useNotifications())
|
const { loadNotifications, getUnreadNotificationCount } = $(useNotifications())
|
||||||
|
const { loadTheme } = useTheme()
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
if (isUserAuthenticated()) {
|
if (isUserAuthenticated()) {
|
||||||
|
loadTheme()
|
||||||
// TODO: reload notifications periodically
|
// TODO: reload notifications periodically
|
||||||
await loadNotifications(ensureAuthToken())
|
await loadNotifications(ensureAuthToken())
|
||||||
}
|
}
|
||||||
|
|
37
src/composables/theme.ts
Normal file
37
src/composables/theme.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import { ref } from "vue"
|
||||||
|
|
||||||
|
const THEME_STORAGE_KEY = "theme"
|
||||||
|
|
||||||
|
enum Theme {
|
||||||
|
Light = "light",
|
||||||
|
Dark = "dark",
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentTheme = ref(Theme.Light)
|
||||||
|
|
||||||
|
export function useTheme() {
|
||||||
|
|
||||||
|
function setTheme(theme: Theme) {
|
||||||
|
document.documentElement.setAttribute("data-theme", theme)
|
||||||
|
currentTheme.value = theme
|
||||||
|
localStorage.setItem(THEME_STORAGE_KEY, theme)
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadTheme() {
|
||||||
|
const theme = localStorage.getItem(THEME_STORAGE_KEY) || Theme.Light
|
||||||
|
setTheme(theme as Theme)
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleDarkMode() {
|
||||||
|
if (currentTheme.value === Theme.Light) {
|
||||||
|
setTheme(Theme.Dark)
|
||||||
|
} else {
|
||||||
|
setTheme(Theme.Light)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
loadTheme,
|
||||||
|
toggleDarkMode,
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,3 +40,38 @@ $shadow-color: #B7B5B5;
|
||||||
|
|
||||||
$gem-color: #9678c3;
|
$gem-color: #9678c3;
|
||||||
$gem-colorizer: invert(51%) sepia(48%) saturate(437%) hue-rotate(222deg) brightness(92%) contrast(84%);
|
$gem-colorizer: invert(51%) sepia(48%) saturate(437%) hue-rotate(222deg) brightness(92%) contrast(84%);
|
||||||
|
|
||||||
|
/* Dark theme */
|
||||||
|
|
||||||
|
$dark-background-color: #12110C;
|
||||||
|
|
||||||
|
$dark-text-color: #cccccc;
|
||||||
|
$dark-text-colorizer: invert(94%) sepia(14%) saturate(0%) hue-rotate(228deg) brightness(83%) contrast(96%);
|
||||||
|
$dark-secondary-text-color: #8C8C8C;
|
||||||
|
$dark-secondary-text-colorizer: invert(62%) sepia(0%) saturate(610%) hue-rotate(189deg) brightness(90%) contrast(86%);
|
||||||
|
$dark-secondary-text-hover-color: #B3B3B3;
|
||||||
|
$dark-secondary-text-hover-colorizer: invert(73%) sepia(1%) saturate(0%) hue-rotate(210deg) brightness(100%) contrast(89%);
|
||||||
|
$dark-link-color: #b7b8b8;
|
||||||
|
$dark-link-colorizer: invert(80%) sepia(4%) saturate(52%) hue-rotate(131deg) brightness(94%) contrast(85%);
|
||||||
|
$dark-link-hover-color: #fff;
|
||||||
|
$dark-link-hover-colorizer: invert(100%) sepia(0%) saturate(7500%) hue-rotate(199deg) brightness(109%) contrast(105%);
|
||||||
|
|
||||||
|
$dark-btn-background-color: $dark-text-color;
|
||||||
|
$dark-btn-background-hover-color: #4D4D4D;
|
||||||
|
$dark-btn-text-color: #000;
|
||||||
|
$dark-btn-text-colorizer: invert(0%) sepia(100%) saturate(0%) hue-rotate(21deg) brightness(97%) contrast(103%);
|
||||||
|
$dark-btn-text-hover-color: $dark-text-color;
|
||||||
|
$dark-btn-text-hover-colorizer: $dark-text-colorizer;
|
||||||
|
$dark-btn-secondary-background-color: #333;
|
||||||
|
$dark-btn-secondary-text-color: $dark-text-color;
|
||||||
|
$dark-btn-disabled-background-color: #222222;
|
||||||
|
$dark-btn-disabled-text-color: #666666;
|
||||||
|
|
||||||
|
$dark-block-background-color: #262626;
|
||||||
|
$dark-block-link-color: #E3C093;
|
||||||
|
$dark-block-link-hover-color: #F2E2CD;
|
||||||
|
$dark-separator-color: $dark-background-color;
|
||||||
|
$dark-widget-background-color: #1A1A1A;
|
||||||
|
$dark-highlight-color: #B37400;
|
||||||
|
$dark-loader-color: #666666;
|
||||||
|
$dark-shadow-color: #666;
|
||||||
|
|
|
@ -64,6 +64,9 @@
|
||||||
<router-link class="btn" :to="{ name: 'move-followers' }">
|
<router-link class="btn" :to="{ name: 'move-followers' }">
|
||||||
Move followers
|
Move followers
|
||||||
</router-link>
|
</router-link>
|
||||||
|
<button class="btn" @click="toggleDarkMode()">
|
||||||
|
Toggle dark mode
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
</section>
|
</section>
|
||||||
|
@ -76,9 +79,11 @@ import { $, $ref } from "vue/macros"
|
||||||
|
|
||||||
import { changePassword, exportFollowers, exportFollows } from "@/api/settings"
|
import { changePassword, exportFollowers, exportFollows } from "@/api/settings"
|
||||||
import SidebarLayout from "@/components/SidebarLayout.vue"
|
import SidebarLayout from "@/components/SidebarLayout.vue"
|
||||||
|
import { useTheme } from "@/composables/theme"
|
||||||
import { useCurrentUser } from "@/store/user"
|
import { useCurrentUser } from "@/store/user"
|
||||||
|
|
||||||
const { currentUser, ensureAuthToken, setCurrentUser } = $(useCurrentUser())
|
const { currentUser, ensureAuthToken, setCurrentUser } = $(useCurrentUser())
|
||||||
|
const { toggleDarkMode } = useTheme()
|
||||||
let newPassword = $ref("")
|
let newPassword = $ref("")
|
||||||
let newPasswordConfirmation = $ref("")
|
let newPasswordConfirmation = $ref("")
|
||||||
let passwordFormMessage = $ref<string | null>(null)
|
let passwordFormMessage = $ref<string | null>(null)
|
||||||
|
|
Loading…
Reference in a new issue