feat(ui): establish theme tokens and global base styles
This commit is contained in:
parent
83e2b95501
commit
f42bd53cff
|
|
@ -8,7 +8,6 @@ import { ErrorBoundary } from './components/ErrorBoundary';
|
|||
import { ToastContainer } from './components/Toast';
|
||||
import { useToast } from './hooks/useToast';
|
||||
import { createContext, useContext } from 'react';
|
||||
import { colors, radius } from './theme';
|
||||
|
||||
// Create toast context to share toast functionality across the app
|
||||
interface ToastContextType {
|
||||
|
|
@ -48,10 +47,10 @@ function App() {
|
|||
return (
|
||||
<ErrorBoundary>
|
||||
<ToastContext.Provider value={toast}>
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<div className="min-h-screen">
|
||||
<ToastContainer messages={toast.messages} onClose={toast.removeToast} />
|
||||
|
||||
<header className="bg-white shadow-sm border-b border-gray-100 ">
|
||||
<header className="sticky top-0 z-20 border-b border-slate-200/70 bg-white/85 shadow-sm backdrop-blur-md dark:border-slate-700/60 dark:bg-slate-900/70">
|
||||
<div className="max-w-7xl mx-auto px-4">
|
||||
<div className="flex items-center justify-between h-16">
|
||||
<div className="flex items-center">
|
||||
|
|
@ -85,7 +84,7 @@ function App() {
|
|||
</Routes>
|
||||
</main>
|
||||
|
||||
<footer className="bg-white border-t border-gray-100 mt-12">
|
||||
<footer className="mt-12 border-t border-slate-200/70 bg-white/70 backdrop-blur-sm dark:border-slate-700/60 dark:bg-slate-900/45">
|
||||
<div className="max-w-7xl mx-auto py-6 px-4">
|
||||
<p className="text-center text-sm text-gray-500">
|
||||
Recipe Manager MVP - Built with React + Vite + TypeScript
|
||||
|
|
|
|||
|
|
@ -3,20 +3,39 @@
|
|||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
--text: #374151;
|
||||
--text-h: #1e293b;
|
||||
--bg: #fff;
|
||||
--bg-alt: #f9fafb;
|
||||
--border: #e5e7eb;
|
||||
--code-bg: #f4f3ec;
|
||||
--accent: #aa3bff;
|
||||
--accent-bg: rgba(170, 59, 255, 0.08);
|
||||
--accent-border: rgba(170, 59, 255, 0.35);
|
||||
--card-shadow: 0 2px 8px 0 rgba(28,30,34,0.08);
|
||||
--color-primary: #2563eb;
|
||||
--color-primary-dark: #1d4ed8;
|
||||
--color-primary-light: #dbeafe;
|
||||
--color-accent: #9333ea;
|
||||
|
||||
--sans: system-ui, 'Segoe UI', Roboto, sans-serif;
|
||||
--heading: system-ui, 'Segoe UI', Roboto, sans-serif;
|
||||
--mono: ui-monospace, Consolas, monospace;
|
||||
--text: #1f2937;
|
||||
--text-h: #0f172a;
|
||||
--text-dim: #64748b;
|
||||
|
||||
--bg: #f4f7fb;
|
||||
--bg-alt: #edf2f7;
|
||||
--surface: #ffffff;
|
||||
--surface-muted: #f8fafc;
|
||||
--border: #dbe3ef;
|
||||
--code-bg: #eef2f7;
|
||||
|
||||
--radius-sm: 0.5rem;
|
||||
--radius-md: 0.75rem;
|
||||
--radius-lg: 1rem;
|
||||
|
||||
--shadow-subtle: 0 1px 2px rgba(15, 23, 42, 0.06);
|
||||
--card-shadow: 0 10px 30px rgba(15, 23, 42, 0.08);
|
||||
--shadow-hover: 0 14px 34px rgba(15, 23, 42, 0.12);
|
||||
--focus-ring: 0 0 0 3px rgba(37, 99, 235, 0.25);
|
||||
|
||||
--surface-gradient:
|
||||
radial-gradient(1200px 500px at -10% -10%, rgba(147, 51, 234, 0.1), transparent 60%),
|
||||
radial-gradient(900px 420px at 115% -5%, rgba(37, 99, 235, 0.08), transparent 52%),
|
||||
linear-gradient(180deg, #f8fbff 0%, #edf2f7 100%);
|
||||
|
||||
--sans: Inter, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
|
||||
--heading: Inter, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
|
||||
--mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
|
|
@ -26,16 +45,25 @@
|
|||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--text: #d1d5db;
|
||||
--text-h: #f3f4f6;
|
||||
--bg: #16171d;
|
||||
--bg-alt: #1a1b20;
|
||||
--border: #2e303a;
|
||||
--code-bg: #1f2028;
|
||||
--accent: #c084fc;
|
||||
--accent-bg: rgba(192, 132, 252, 0.11);
|
||||
--accent-border: rgba(192, 132, 252, 0.33);
|
||||
--card-shadow: 0 3px 14px 0 rgba(32,34,40,0.21);
|
||||
--text: #e2e8f0;
|
||||
--text-h: #f8fafc;
|
||||
--text-dim: #94a3b8;
|
||||
|
||||
--bg: #0f172a;
|
||||
--bg-alt: #111b2e;
|
||||
--surface: #132136;
|
||||
--surface-muted: #172842;
|
||||
--border: #22334d;
|
||||
--code-bg: #1a2942;
|
||||
|
||||
--shadow-subtle: 0 1px 2px rgba(2, 6, 23, 0.35);
|
||||
--card-shadow: 0 10px 30px rgba(2, 6, 23, 0.45);
|
||||
--shadow-hover: 0 14px 34px rgba(2, 6, 23, 0.55);
|
||||
|
||||
--surface-gradient:
|
||||
radial-gradient(900px 400px at 0% -5%, rgba(147, 51, 234, 0.2), transparent 60%),
|
||||
radial-gradient(800px 420px at 105% -10%, rgba(37, 99, 235, 0.18), transparent 55%),
|
||||
linear-gradient(180deg, #0f172a 0%, #111b2e 100%);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -43,56 +71,125 @@ body {
|
|||
margin: 0;
|
||||
font-family: var(--sans);
|
||||
color: var(--text);
|
||||
background: var(--bg-alt);
|
||||
background: var(--surface-gradient);
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
||||
#root {
|
||||
min-height: 100vh;
|
||||
background: var(--bg-alt);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
input,
|
||||
button,
|
||||
textarea,
|
||||
select {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: var(--heading);
|
||||
color: var(--text-h);
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
button,
|
||||
.button,
|
||||
.btn {
|
||||
border-radius: var(--radius-md);
|
||||
border: 1px solid transparent;
|
||||
transition: background-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease,
|
||||
transform 0.15s ease;
|
||||
}
|
||||
|
||||
button:hover,
|
||||
.button:hover,
|
||||
.btn:hover {
|
||||
box-shadow: var(--shadow-subtle);
|
||||
}
|
||||
|
||||
button:focus-visible,
|
||||
.button:focus-visible,
|
||||
.btn:focus-visible,
|
||||
input:focus-visible,
|
||||
textarea:focus-visible,
|
||||
select:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
|
||||
input,
|
||||
textarea,
|
||||
select {
|
||||
border-radius: var(--radius-md);
|
||||
border: 1px solid var(--border);
|
||||
background: var(--surface);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
input::placeholder,
|
||||
textarea::placeholder {
|
||||
color: color-mix(in srgb, var(--text-dim) 70%, transparent);
|
||||
}
|
||||
|
||||
.card,
|
||||
.shadow-card {
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--border);
|
||||
background: var(--surface);
|
||||
box-shadow: var(--card-shadow);
|
||||
}
|
||||
|
||||
.shadow-card {
|
||||
box-shadow: var(--card-shadow) !important;
|
||||
}
|
||||
|
||||
/* Toast animation */
|
||||
@keyframes slide-in {
|
||||
from { transform: translateX(100%); opacity: 0; }
|
||||
to { transform: translateX(0); opacity: 1; }
|
||||
from {
|
||||
transform: translateX(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.animate-slide-in {
|
||||
animation: slide-in 0.3s ease-out;
|
||||
}
|
||||
|
||||
/* Recipe Manager visual polish */
|
||||
|
||||
::-webkit-input-placeholder { color: #96a3b7; }
|
||||
::-moz-placeholder { color: #96a3b7; }
|
||||
:-ms-input-placeholder { color: #96a3b7; }
|
||||
::placeholder { color: #96a3b7; }
|
||||
|
||||
input:focus, textarea:focus, select:focus { outline: 2px solid #3b82f6; outline-offset: 2px; }
|
||||
|
||||
button, .button, .btn {
|
||||
transition: box-shadow 0.13s, background 0.13s, color 0.13s;
|
||||
}
|
||||
|
||||
.card, .shadow-card { border-radius: 1rem; box-shadow: var(--card-shadow); }
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
background: #f3f4f6;
|
||||
background: var(--surface-muted);
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #e5e7eb;
|
||||
background: var(--border);
|
||||
border-radius: 7px;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.max-w-7xl, .max-w-6xl, .max-w-4xl, .max-w-3xl, .max-w-2xl, .max-w-xl, .max-w-md, .max-w-sm { max-width: 100vw !important; }
|
||||
.p-8, .p-7, .p-6 { padding: 1rem !important; }
|
||||
.max-w-7xl,
|
||||
.max-w-6xl,
|
||||
.max-w-4xl,
|
||||
.max-w-3xl,
|
||||
.max-w-2xl,
|
||||
.max-w-xl,
|
||||
.max-w-md,
|
||||
.max-w-sm {
|
||||
max-width: 100vw !important;
|
||||
}
|
||||
.p-8,
|
||||
.p-7,
|
||||
.p-6 {
|
||||
padding: 1rem !important;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,54 +1,80 @@
|
|||
/** theme.ts - Defines visual theme tokens and utility styles across the Recipe Manager frontend */
|
||||
/**
|
||||
* Centralized design tokens for the Recipe Manager frontend.
|
||||
* Keep values semantic so UI primitives can evolve without broad refactors.
|
||||
*/
|
||||
|
||||
export const colors = {
|
||||
primary: '#2563eb', // Tailwind blue-600
|
||||
primary: '#2563eb',
|
||||
primaryDark: '#1d4ed8',
|
||||
primaryLight: '#eff6ff',
|
||||
accent: '#aa3bff',
|
||||
success: '#16a34a',
|
||||
warning: '#eab308',
|
||||
primaryLight: '#dbeafe',
|
||||
accent: '#9333ea',
|
||||
success: '#15803d',
|
||||
warning: '#ca8a04',
|
||||
error: '#dc2626',
|
||||
|
||||
bg: '#fff',
|
||||
bgAlt: '#f9fafb', // Tailwind gray-50
|
||||
surface: '#fcfcff',
|
||||
border: '#e5e7eb', // Tailwind gray-200
|
||||
text: '#374151', // Tailwind gray-700
|
||||
textDim: '#6b7280',
|
||||
textHeading: '#1e293b',
|
||||
cardShadow: '0 2px 8px 0 rgba(28,30,34,0.08)',
|
||||
};
|
||||
bg: '#f4f7fb',
|
||||
bgAlt: '#edf2f7',
|
||||
surface: '#ffffff',
|
||||
surfaceMuted: '#f8fafc',
|
||||
border: '#dbe3ef',
|
||||
|
||||
export const radius = {
|
||||
xs: '4px',
|
||||
sm: '6px',
|
||||
md: '10px',
|
||||
lg: '16px',
|
||||
full: '999px',
|
||||
};
|
||||
text: '#1f2937',
|
||||
textDim: '#64748b',
|
||||
textHeading: '#0f172a',
|
||||
|
||||
export const spacing = {
|
||||
xs: '4px',
|
||||
sm: '8px',
|
||||
md: '16px',
|
||||
lg: '24px',
|
||||
xl: '40px',
|
||||
};
|
||||
|
||||
export const shadows = {
|
||||
card: '0 2px 8px 0 rgba(28,30,34,0.08)',
|
||||
hover: '0 4px 20px 0 rgba(28,30,34,0.16)',
|
||||
focusRing: '#2563eb',
|
||||
};
|
||||
|
||||
export const typography = {
|
||||
fontFamily: {
|
||||
sans: 'system-ui, Segoe UI, Roboto, sans-serif',
|
||||
heading: 'system-ui, Segoe UI, Roboto, sans-serif',
|
||||
mono: 'ui-monospace, Consolas, monospace',
|
||||
sans: "Inter, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif",
|
||||
heading: "Inter, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif",
|
||||
mono: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
|
||||
},
|
||||
fontSize: {
|
||||
xs: '0.75rem',
|
||||
sm: '0.875rem',
|
||||
base: '1rem',
|
||||
lg: '1.125rem',
|
||||
xl: '1.25rem',
|
||||
'2xl': '1.5rem',
|
||||
'3xl': '1.875rem',
|
||||
},
|
||||
lineHeight: {
|
||||
tight: '1.2',
|
||||
normal: '1.5',
|
||||
relaxed: '1.65',
|
||||
},
|
||||
fontWeight: {
|
||||
regular: 400,
|
||||
medium: 500,
|
||||
semibold: 600,
|
||||
bold: 700,
|
||||
},
|
||||
};
|
||||
|
||||
export const spacing = {
|
||||
xxs: '0.25rem',
|
||||
xs: '0.5rem',
|
||||
sm: '0.75rem',
|
||||
md: '1rem',
|
||||
lg: '1.5rem',
|
||||
xl: '2rem',
|
||||
'2xl': '2.5rem',
|
||||
};
|
||||
|
||||
export const radius = {
|
||||
xs: '0.375rem',
|
||||
sm: '0.5rem',
|
||||
md: '0.75rem',
|
||||
lg: '1rem',
|
||||
xl: '1.25rem',
|
||||
full: '9999px',
|
||||
};
|
||||
|
||||
export const shadows = {
|
||||
subtle: '0 1px 2px rgba(15, 23, 42, 0.06)',
|
||||
card: '0 10px 30px rgba(15, 23, 42, 0.08)',
|
||||
hover: '0 14px 34px rgba(15, 23, 42, 0.12)',
|
||||
focus: '0 0 0 3px rgba(37, 99, 235, 0.25)',
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue