Edit the light mode
This commit is contained in:
parent
346fb57e39
commit
f5cd32bd58
@ -557,3 +557,154 @@ select {
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Theme Toggle (fixed floating button) */
|
||||||
|
.theme-toggle {
|
||||||
|
position: fixed;
|
||||||
|
top: 1.5rem;
|
||||||
|
right: 1.5rem;
|
||||||
|
z-index: 100;
|
||||||
|
width: 3rem;
|
||||||
|
height: 3rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 1px solid var(--border-subtle);
|
||||||
|
background: var(--card);
|
||||||
|
color: var(--text-main);
|
||||||
|
font-size: 1.2rem;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
|
||||||
|
transition: all 180ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-toggle:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-toggle:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update body to apply bg properly in both themes */
|
||||||
|
body {
|
||||||
|
background: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] body {
|
||||||
|
background: radial-gradient(circle at top, #0f172a 0, #020617 55%);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] {
|
||||||
|
--bg: #f9fafb7a;
|
||||||
|
--bg-elevated: #ffffff7c;
|
||||||
|
--card: #ffffff54;
|
||||||
|
--card-soft: #f3f4f6;
|
||||||
|
--border-subtle: rgba(107, 114, 128, 0.25);
|
||||||
|
--accent: #059669;
|
||||||
|
--accent-soft: rgba(5, 150, 105, 0.12);
|
||||||
|
--accent-strong: #047857;
|
||||||
|
--text-main: #1f2937;
|
||||||
|
--text-muted: #6b7280;
|
||||||
|
--danger: #dc2626;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] body {
|
||||||
|
background: linear-gradient(180deg, #ac8d75 0%, #f6f8fa 100%);
|
||||||
|
color: var(--text-main);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] .topbar {
|
||||||
|
background: linear-gradient(90deg, #e7be9e, #e2b08a);
|
||||||
|
border: 1px solid rgba(107, 114, 128, 0.3);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] .btn.primary {
|
||||||
|
background: linear-gradient(135deg, var(--accent), var(--accent-strong));
|
||||||
|
color: #ffffff;
|
||||||
|
box-shadow: 0 4px 12px rgba(5, 150, 105, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] .btn.accent {
|
||||||
|
background: var(--accent-soft);
|
||||||
|
color: #065f46;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] .btn.accent:hover {
|
||||||
|
background: rgba(5, 150, 105, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] .btn.ghost {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--text-main);
|
||||||
|
border: 1px solid rgba(107, 114, 128, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] .btn.ghost:hover {
|
||||||
|
background: rgba(107, 114, 128, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] .btn.danger {
|
||||||
|
background: rgba(220, 38, 38, 0.12);
|
||||||
|
color: #991b1b;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] .btn.danger:hover {
|
||||||
|
background: rgba(220, 38, 38, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] input,
|
||||||
|
[data-theme="light"] select {
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 1px solid rgba(107, 114, 128, 0.3);
|
||||||
|
background: #d4cfcf;
|
||||||
|
color: var(--text-main);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] input::placeholder {
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] option {
|
||||||
|
background: #ffffff;
|
||||||
|
color: var(--text-main);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] .panel {
|
||||||
|
background: var(--card);
|
||||||
|
border: 1px solid rgba(107, 114, 128, 0.2);
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] .modal {
|
||||||
|
background: var(--card);
|
||||||
|
border: 1px solid rgba(107, 114, 128, 0.2);
|
||||||
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] .drawer {
|
||||||
|
background: #ffffff;
|
||||||
|
border-left: 1px solid rgba(107, 114, 128, 0.2);
|
||||||
|
box-shadow: -4px 0 15px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] .error-banner {
|
||||||
|
background: rgba(220, 38, 38, 0.08);
|
||||||
|
border: 1px solid rgba(248, 113, 113, 0.5);
|
||||||
|
color: #991b1b;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] .pill {
|
||||||
|
background: rgba(107, 114, 128, 0.1);
|
||||||
|
border: 1px solid rgba(107, 114, 128, 0.3);
|
||||||
|
color: var(--text-main);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] .tag {
|
||||||
|
background: rgba(107, 114, 128, 0.1);
|
||||||
|
border: 1px solid rgba(107, 114, 128, 0.3);
|
||||||
|
color: var(--text-main);
|
||||||
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import RecipeDetails from "./components/RecipeDetails";
|
|||||||
import RecipeFormDrawer from "./components/RecipeFormDrawer";
|
import RecipeFormDrawer from "./components/RecipeFormDrawer";
|
||||||
import Modal from "./components/Modal";
|
import Modal from "./components/Modal";
|
||||||
import ToastContainer from "./components/ToastContainer";
|
import ToastContainer from "./components/ToastContainer";
|
||||||
|
import ThemeToggle from "./components/ThemeToggle";
|
||||||
import { getRecipes, getRandomRecipe, createRecipe, updateRecipe, deleteRecipe } from "./api";
|
import { getRecipes, getRandomRecipe, createRecipe, updateRecipe, deleteRecipe } from "./api";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
@ -25,11 +26,25 @@ function App() {
|
|||||||
|
|
||||||
const [deleteModal, setDeleteModal] = useState({ isOpen: false, recipeId: null, recipeName: "" });
|
const [deleteModal, setDeleteModal] = useState({ isOpen: false, recipeId: null, recipeName: "" });
|
||||||
const [toasts, setToasts] = useState([]);
|
const [toasts, setToasts] = useState([]);
|
||||||
|
const [theme, setTheme] = useState(() => {
|
||||||
|
try {
|
||||||
|
return localStorage.getItem("theme") || "dark";
|
||||||
|
} catch {
|
||||||
|
return "dark";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadRecipes();
|
loadRecipes();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.documentElement.setAttribute("data-theme", theme);
|
||||||
|
try {
|
||||||
|
localStorage.setItem("theme", theme);
|
||||||
|
} catch {}
|
||||||
|
}, [theme]);
|
||||||
|
|
||||||
const loadRecipes = async () => {
|
const loadRecipes = async () => {
|
||||||
try {
|
try {
|
||||||
const list = await getRecipes();
|
const list = await getRecipes();
|
||||||
@ -147,6 +162,7 @@ function App() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="app-root">
|
<div className="app-root">
|
||||||
|
<ThemeToggle theme={theme} onToggleTheme={() => setTheme((t) => (t === "dark" ? "light" : "dark"))} />
|
||||||
<TopBar onAddClick={() => setDrawerOpen(true)} />
|
<TopBar onAddClick={() => setDrawerOpen(true)} />
|
||||||
|
|
||||||
<main className="layout">
|
<main className="layout">
|
||||||
@ -210,7 +226,6 @@ function App() {
|
|||||||
<RecipeDetails
|
<RecipeDetails
|
||||||
recipe={selectedRecipe}
|
recipe={selectedRecipe}
|
||||||
onEditClick={handleEditRecipe}
|
onEditClick={handleEditRecipe}
|
||||||
onDeleteClick={handleShowDeleteModal}
|
|
||||||
onShowDeleteModal={handleShowDeleteModal}
|
onShowDeleteModal={handleShowDeleteModal}
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -16,7 +16,7 @@ function RecipeDetails({ recipe, onEditClick, onDeleteClick, onShowDeleteModal }
|
|||||||
<header className="recipe-header">
|
<header className="recipe-header">
|
||||||
<div>
|
<div>
|
||||||
<h2>{recipe.name}</h2>
|
<h2>{recipe.name}</h2>
|
||||||
<p className="recipe-subtitle">qwwa
|
<p className="recipe-subtitle">
|
||||||
{translateMealType(recipe.meal_type)} · {recipe.time_minutes} דקות הכנה
|
{translateMealType(recipe.meal_type)} · {recipe.time_minutes} דקות הכנה
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
14
frontend/src/components/ThemeToggle.jsx
Normal file
14
frontend/src/components/ThemeToggle.jsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
function ThemeToggle({ theme, onToggleTheme }) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className="theme-toggle"
|
||||||
|
title={theme === "dark" ? "הפעל מצב בהיר" : "הפעל מצב חשוך"}
|
||||||
|
onClick={onToggleTheme}
|
||||||
|
aria-label="Toggle theme"
|
||||||
|
>
|
||||||
|
{theme === "dark" ? "🌤" : "🌙"}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ThemeToggle;
|
||||||
@ -11,9 +11,11 @@ function TopBar({ onAddClick }) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button className="btn primary" onClick={onAddClick}>
|
<div style={{ display: "flex", gap: "0.6rem", alignItems: "center" }}>
|
||||||
+ מתכון חדש
|
<button className="btn primary" onClick={onAddClick}>
|
||||||
</button>
|
+ מתכון חדש
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user