Theme select & Accessability :^)
All checks were successful
Docker Deploy / build-and-push (push) Successful in 4m0s
All checks were successful
Docker Deploy / build-and-push (push) Successful in 4m0s
This commit is contained in:
@@ -11,7 +11,7 @@ export default defineConfig({
|
|||||||
output: "server",
|
output: "server",
|
||||||
integrations: [vue(), icon()],
|
integrations: [vue(), icon()],
|
||||||
security: {
|
security: {
|
||||||
csp: true,
|
csp: process.env.NODE_ENV === "production",
|
||||||
},
|
},
|
||||||
vite: {
|
vite: {
|
||||||
plugins: [tailwindcss()],
|
plugins: [tailwindcss()],
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "chronus",
|
"name": "chronus",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "2.2.1",
|
"version": "2.3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "astro dev",
|
"dev": "astro dev",
|
||||||
"build": "astro build",
|
"build": "astro build",
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
"migrate": "node scripts/migrate.js"
|
"migrate": "node scripts/migrate.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/check": "0.9.6-beta.1",
|
"@astrojs/check": "0.9.6",
|
||||||
"@astrojs/node": "10.0.0-beta.0",
|
"@astrojs/node": "10.0.0-beta.0",
|
||||||
"@astrojs/vue": "6.0.0-beta.0",
|
"@astrojs/vue": "6.0.0-beta.0",
|
||||||
"@ceereals/vue-pdf": "^0.2.1",
|
"@ceereals/vue-pdf": "^0.2.1",
|
||||||
|
|||||||
459
pnpm-lock.yaml
generated
459
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
34
src/components/ThemeToggle.vue
Normal file
34
src/components/ThemeToggle.vue
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { Icon } from '@iconify/vue';
|
||||||
|
|
||||||
|
const theme = ref('macchiato');
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const stored = localStorage.getItem('theme');
|
||||||
|
if (stored) {
|
||||||
|
theme.value = stored;
|
||||||
|
document.documentElement.setAttribute('data-theme', stored);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function toggleTheme() {
|
||||||
|
const newTheme = theme.value === 'macchiato' ? 'latte' : 'macchiato';
|
||||||
|
theme.value = newTheme;
|
||||||
|
document.documentElement.setAttribute('data-theme', newTheme);
|
||||||
|
localStorage.setItem('theme', newTheme);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<button
|
||||||
|
@click="toggleTheme"
|
||||||
|
class="btn btn-ghost btn-circle"
|
||||||
|
aria-label="Toggle Theme"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
:icon="theme === 'macchiato' ? 'heroicons:moon' : 'heroicons:sun'"
|
||||||
|
class="w-5 h-5"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
@@ -5,6 +5,7 @@ import { db } from '../db';
|
|||||||
import { members, organizations } from '../db/schema';
|
import { members, organizations } from '../db/schema';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import Avatar from '../components/Avatar.astro';
|
import Avatar from '../components/Avatar.astro';
|
||||||
|
import ThemeToggle from '../components/ThemeToggle.vue';
|
||||||
import { ClientRouter } from "astro:transitions";
|
import { ClientRouter } from "astro:transitions";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -32,7 +33,7 @@ const currentTeam = userMemberships.find(m => m.organization.id === currentTeamI
|
|||||||
---
|
---
|
||||||
|
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en" data-theme="dark">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="description" content="Chronus Dashboard" />
|
<meta name="description" content="Chronus Dashboard" />
|
||||||
@@ -41,6 +42,10 @@ const currentTeam = userMemberships.find(m => m.organization.id === currentTeamI
|
|||||||
<meta name="generator" content={Astro.generator} />
|
<meta name="generator" content={Astro.generator} />
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
<ClientRouter />
|
<ClientRouter />
|
||||||
|
<script is:inline>
|
||||||
|
const theme = localStorage.getItem('theme') || 'macchiato';
|
||||||
|
document.documentElement.setAttribute('data-theme', theme);
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-base-100 h-screen flex flex-col overflow-hidden">
|
<body class="bg-base-100 h-screen flex flex-col overflow-hidden">
|
||||||
<div class="drawer lg:drawer-open flex-1 overflow-auto">
|
<div class="drawer lg:drawer-open flex-1 overflow-auto">
|
||||||
@@ -57,6 +62,9 @@ const currentTeam = userMemberships.find(m => m.organization.id === currentTeamI
|
|||||||
<img src="/logo.webp" alt="Chronus" class="h-8 w-8" />
|
<img src="/logo.webp" alt="Chronus" class="h-8 w-8" />
|
||||||
<span class="text-xl font-bold text-primary">Chronus</span>
|
<span class="text-xl font-bold text-primary">Chronus</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex-none">
|
||||||
|
<ThemeToggle client:load />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Page content here -->
|
<!-- Page content here -->
|
||||||
@@ -177,6 +185,13 @@ const currentTeam = userMemberships.find(m => m.organization.id === currentTeamI
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<div class="flex justify-between items-center p-2 hover:bg-transparent">
|
||||||
|
<span class="font-semibold text-sm text-base-content/70 pl-2">Theme</span>
|
||||||
|
<ThemeToggle client:load />
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<form action="/api/auth/logout" method="POST" class="contents">
|
<form action="/api/auth/logout" method="POST" class="contents">
|
||||||
<button type="submit" class="flex w-full items-center gap-2 py-2 px-4 text-error hover:bg-error/10 rounded-lg transition-colors active:bg-base-300/50!">
|
<button type="submit" class="flex w-full items-center gap-2 py-2 px-4 text-error hover:bg-error/10 rounded-lg transition-colors active:bg-base-300/50!">
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const { title } = Astro.props;
|
|||||||
---
|
---
|
||||||
|
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en" data-theme="dark">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="description" content="Chronus Time Tracking" />
|
<meta name="description" content="Chronus Time Tracking" />
|
||||||
@@ -19,6 +19,10 @@ const { title } = Astro.props;
|
|||||||
<meta name="generator" content={Astro.generator} />
|
<meta name="generator" content={Astro.generator} />
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
<ClientRouter />
|
<ClientRouter />
|
||||||
|
<script is:inline>
|
||||||
|
const theme = localStorage.getItem('theme') || 'macchiato';
|
||||||
|
document.documentElement.setAttribute('data-theme', theme);
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body class="min-h-screen bg-base-100 text-base-content flex flex-col">
|
<body class="min-h-screen bg-base-100 text-base-content flex flex-col">
|
||||||
<div class="flex-1 flex flex-col">
|
<div class="flex-1 flex flex-col">
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ const errorMessage =
|
|||||||
name="email"
|
name="email"
|
||||||
placeholder="your@email.com"
|
placeholder="your@email.com"
|
||||||
class="input input-bordered w-full"
|
class="input input-bordered w-full"
|
||||||
|
autocomplete="email"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -56,6 +57,7 @@ const errorMessage =
|
|||||||
name="password"
|
name="password"
|
||||||
placeholder="Enter your password"
|
placeholder="Enter your password"
|
||||||
class="input input-bordered w-full"
|
class="input input-bordered w-full"
|
||||||
|
autocomplete="current-password"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ const errorMessage =
|
|||||||
name="name"
|
name="name"
|
||||||
placeholder="John Doe"
|
placeholder="John Doe"
|
||||||
class="input input-bordered w-full"
|
class="input input-bordered w-full"
|
||||||
|
autocomplete="name"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -88,6 +89,7 @@ const errorMessage =
|
|||||||
name="email"
|
name="email"
|
||||||
placeholder="your@email.com"
|
placeholder="your@email.com"
|
||||||
class="input input-bordered w-full"
|
class="input input-bordered w-full"
|
||||||
|
autocomplete="email"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -102,6 +104,7 @@ const errorMessage =
|
|||||||
name="password"
|
name="password"
|
||||||
placeholder="Create a strong password"
|
placeholder="Create a strong password"
|
||||||
class="input input-bordered w-full"
|
class="input input-bordered w-full"
|
||||||
|
autocomplete="new-password"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,4 +2,5 @@
|
|||||||
@plugin "daisyui" {
|
@plugin "daisyui" {
|
||||||
themes: false;
|
themes: false;
|
||||||
}
|
}
|
||||||
@plugin "./theme.ts";
|
@plugin "./theme-dark.ts";
|
||||||
|
@plugin "./theme-light.ts";
|
||||||
|
|||||||
9
src/styles/theme-light.ts
Normal file
9
src/styles/theme-light.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { createCatppuccinPlugin } from "@catppuccin/daisyui";
|
||||||
|
|
||||||
|
export default createCatppuccinPlugin(
|
||||||
|
"latte",
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user