commit 39eefbde4d451dc89ff2c216d4e8d9268c86d879
parent 6e559640019d77d2219bd406fca4b5888ed0f06a
Author: Sunny <kajimalu10@gmail.com>
Date: Thu, 14 Aug 2025 10:40:32 +0900
adjust style
Diffstat:
8 files changed, 146 insertions(+), 26 deletions(-)
diff --git a/src/app/globals.css b/src/app/globals.css
@@ -21,24 +21,22 @@
--muted-foreground: 107 114 128; /* Subtle text */
}
-@media (prefers-color-scheme: dark) {
- :root {
- /* ダークモードの色定義 (RGB値) */
- --background: 18 18 18;
- --foreground: 250 250 250;
+.dark {
+ /* ダークモードの色定義 (RGB値) */
+ --background: 18 18 18;
+ --foreground: 250 250 250;
- --card: 24 24 27;
- --card-foreground: 250 250 250;
+ --card: 24 24 27;
+ --card-foreground: 250 250 250;
- --primary: 59 130 246;
- --primary-foreground: 250 250 250;
+ --primary: 59 130 246;
+ --primary-foreground: 250 250 250;
- --secondary: 30 30 33;
- --secondary-foreground: 250 250 250;
+ --secondary: 30 30 33;
+ --secondary-foreground: 250 250 250;
- --muted: 30 30 33;
- --muted-foreground: 156 163 175;
- }
+ --muted: 30 30 33;
+ --muted-foreground: 156 163 175;
}
/* ================================== */
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
@@ -3,6 +3,7 @@
import { useState } from 'react';
import { GoogleOAuthProvider } from '@react-oauth/google';
import { AuthProvider, useAuth } from '@/hooks/useAuth';
+import { ThemeProvider } from '@/hooks/useTheme';
import Header from '@/components/Header';
import SidePane from '@/components/SidePane';
import "./globals.css";
@@ -37,7 +38,9 @@ export default function RootLayout({ children }: { children: React.ReactNode })
<body>
<GoogleOAuthProvider clientId={clientId}>
<AuthProvider>
- <AppLayout>{children}</AppLayout>
+ <ThemeProvider>
+ <AppLayout>{children}</AppLayout>
+ </ThemeProvider>
</AuthProvider>
</GoogleOAuthProvider>
</body>
diff --git a/src/app/page.tsx b/src/app/page.tsx
@@ -122,7 +122,7 @@ export default function Home() {
}
return (
- <main className="flex flex-col items-center p-4 sm:p-8 text-gray-800">
+ <main className="flex flex-col items-center p-4 sm:p-8 text-foreground">
<div className="w-full max-w-md mx-auto">
{!user ? (
<div className="card text-center">
diff --git a/src/components/Header.tsx b/src/components/Header.tsx
@@ -1,5 +1,7 @@
"use client";
+import ThemeToggleButton from './ThemeToggleButton';
+
// ハンバーガーメニューアイコン
const MenuIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
@@ -8,16 +10,19 @@ const MenuIcon = () => (
);
interface HeaderProps {
- onMenuClick: () => void; // メニューボタンがクリックされたことを親に伝える
+ onMenuClick: () => void;
}
export default function Header({ onMenuClick }: HeaderProps) {
return (
- <header className="fixed top-0 left-0 right-0 z-20 flex items-center justify-between p-4 bg-slate-100/80 backdrop-blur-sm shadow-sm">
- <h1 className="text-xl font-bold text-blue-600">カフェタイマー</h1>
- <button onClick={onMenuClick} className="p-2">
- <MenuIcon />
- </button>
+ <header className="fixed top-0 left-0 right-0 z-20 flex items-center h-12 justify-between p-4 backdrop-blur-md shadow-md">
+ <h1 className="text-lg font-bold text-foreground">カフェタイマー</h1>
+ <div className="flex items-center gap-2">
+ <ThemeToggleButton />
+ <button onClick={onMenuClick} className="p-2 rounded-md hover:bg-secondary">
+ <MenuIcon />
+ </button>
+ </div>
</header>
);
}
\ No newline at end of file
diff --git a/src/components/StatsGraph.tsx b/src/components/StatsGraph.tsx
@@ -60,8 +60,28 @@ export default function StatsGraph({ tapHistory }: StatsGraphProps) {
const commonOptions: ChartOptions<'line'> = {
responsive: true,
maintainAspectRatio: false,
- plugins: { legend: { position: 'top' } },
- scales: { y: { beginAtZero: true, ticks: { stepSize: 2 } } },
+ plugins: {
+ legend: {
+ position: 'top',
+ labels: {
+ color: '#999999'
+ }
+ }
+ },
+ scales: {
+ y: {
+ beginAtZero: true,
+ ticks: {
+ stepSize: 2,
+ color: '#999999',
+ },
+ },
+ x: {
+ ticks: {
+ color: '#999999',
+ },
+ },
+ },
};
let navLabelText = '';
@@ -125,7 +145,7 @@ export default function StatsGraph({ tapHistory }: StatsGraphProps) {
<div className="space-y-6">
{/* サマリー表示 */}
<div>
- <h3 className="text-lg font-bold mb-2 text-slate-700">サマリー</h3>
+ <h3 className="text-lg font-bold mb-2 text-foreground">サマリー</h3>
<div className="grid grid-cols-2 gap-4 text-center">
<div className="p-4 bg-secondary rounded-lg">
<div className="text-sm text-muted-foreground">今日</div>
@@ -148,7 +168,7 @@ export default function StatsGraph({ tapHistory }: StatsGraphProps) {
{/* グラフ表示エリア */}
<div>
- <h3 className="text-lg font-bold mb-2 text-slate-700">タップ履歴グラフ</h3>
+ <h3 className="text-lg font-bold mb-2 text-foreground">タップ履歴グラフ</h3>
<div className="my-4 flex justify-center gap-2">
<button
onClick={() => setView('daily')}
diff --git a/src/components/ThemeToggleButton.tsx b/src/components/ThemeToggleButton.tsx
@@ -0,0 +1,30 @@
+"use client";
+
+import { useTheme } from '@/hooks/useTheme';
+
+const SunIcon = () => (
+ <svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
+ <path strokeLinecap="round" strokeLinejoin="round" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
+ </svg>
+);
+
+const MoonIcon = () => (
+ <svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
+ <path strokeLinecap="round" strokeLinejoin="round" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
+ </svg>
+);
+
+
+export default function ThemeToggleButton() {
+ const { theme, setTheme } = useTheme();
+
+ const toggleTheme = () => {
+ setTheme(theme === 'light' ? 'dark' : 'light');
+ };
+
+ return (
+ <button onClick={toggleTheme} className="p-2 rounded-md hover:bg-secondary">
+ {theme === 'light' ? <MoonIcon /> : <SunIcon />}
+ </button>
+ );
+}+
\ No newline at end of file
diff --git a/src/hooks/useTheme.tsx b/src/hooks/useTheme.tsx
@@ -0,0 +1,46 @@
+"use client";
+
+import { createContext, useContext, useEffect, useState, ReactNode } from 'react';
+
+type Theme = 'light' | 'dark';
+
+interface ThemeContextType {
+ theme: Theme;
+ setTheme: (theme: Theme) => void;
+}
+
+const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
+
+export const ThemeProvider = ({ children }: { children: ReactNode }) => {
+ const [theme, setTheme] = useState<Theme>('light');
+
+ useEffect(() => {
+ // 初回読み込み時にlocalStorageからテーマを取得、なければ'light'
+ const storedTheme = localStorage.getItem('theme') as Theme | null;
+ if (storedTheme) {
+ setTheme(storedTheme);
+ }
+ }, []);
+
+ useEffect(() => {
+ // themeが変更されたら<html>のクラスとlocalStorageを更新
+ const root = window.document.documentElement;
+ root.classList.remove('light', 'dark');
+ root.classList.add(theme);
+ localStorage.setItem('theme', theme);
+ }, [theme]);
+
+ return (
+ <ThemeContext.Provider value={{ theme, setTheme }}>
+ {children}
+ </ThemeContext.Provider>
+ );
+};
+
+export const useTheme = () => {
+ const context = useContext(ThemeContext);
+ if (context === undefined) {
+ throw new Error('useTheme must be used within a ThemeProvider');
+ }
+ return context;
+};+
\ No newline at end of file
diff --git a/tailwind.config.ts b/tailwind.config.ts
@@ -0,0 +1,15 @@
+import type { Config } from 'tailwindcss'
+
+const config: Config = {
+
+ darkMode: 'class',
+
+ content: [
+ './src/pages/**/*.{js,ts,jsx,tsx,mdx}',
+ './src/components/**/*.{js,ts,jsx,tsx,mdx}',
+ './src/app/**/*.{js,ts,jsx,tsx,mdx}',
+ ],
+ theme: {},
+ plugins: [],
+}
+export default config+
\ No newline at end of file