diff options
| author | Tyler Hoang <tyler@tylerhoang.xyz> | 2026-05-08 01:58:48 -0700 |
|---|---|---|
| committer | Tyler Hoang <tyler@tylerhoang.xyz> | 2026-05-08 01:58:48 -0700 |
| commit | 2e0a94e88c847a5ed8dc6ad5fa49715cd631bdfe (patch) | |
| tree | 0c27fc5a8d8cbba60e571bb6690a13c0c0060ff4 /static/style.css | |
Initial commit — Commis personal chef app
AI-powered local chef tool: pantry tracking, meal logging, rotating weekly
menu generation, and grocery list optimization via Ollama (llama3).
FastAPI backend, SQLite, vanilla JS frontend.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'static/style.css')
| -rw-r--r-- | static/style.css | 823 |
1 files changed, 823 insertions, 0 deletions
diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..18908c7 --- /dev/null +++ b/static/style.css @@ -0,0 +1,823 @@ +:root { + --bg: #0f1117; + --surface: #1a1d27; + --surface2: #252836; + --border: #2e3148; + --accent: #6c63ff; + --accent-hover: #8b84ff; + --text: #e2e8f0; + --text-muted: #94a3b8; + --success: #22c55e; + --warning: #f59e0b; + --danger: #ef4444; + --info: #3b82f6; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + background-color: var(--bg); + color: var(--text); + line-height: 1.6; +} + +/* ── Header ─────────────────────────────────────────────────────────────── */ +header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1.5rem 2rem; + background-color: var(--surface); + border-bottom: 1px solid var(--border); + position: sticky; + top: 0; + z-index: 100; +} + +.header-title { + font-size: 1.5rem; + font-weight: 600; + letter-spacing: 0.5px; +} + +.status-badge { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem 1rem; + border-radius: 1rem; + background-color: var(--surface2); + border: 1px solid var(--border); + font-size: 0.875rem; + font-weight: 500; +} + +.status-ok { + border-color: var(--success); + color: var(--success); +} + +.status-error { + border-color: var(--danger); + color: var(--danger); +} + +/* ── Tab Navigation ───────────────────────────────────────────────────────── */ +.tab-nav { + display: flex; + gap: 0.5rem; + padding: 1rem 2rem; + background-color: var(--surface); + border-bottom: 1px solid var(--border); + overflow-x: auto; +} + +.tab-btn { + padding: 0.75rem 1.5rem; + background-color: transparent; + border: 1px solid var(--border); + border-radius: 2rem; + color: var(--text-muted); + font-size: 0.9rem; + font-weight: 500; + cursor: pointer; + transition: all 0.2s ease; + white-space: nowrap; +} + +.tab-btn:hover { + border-color: var(--accent); + color: var(--text); +} + +.tab-btn.active { + background-color: var(--accent); + border-color: var(--accent); + color: var(--bg); +} + +/* ── Main Content ───────────────────────────────────────────────────────── */ +main { + max-width: 1400px; + margin: 0 auto; + padding: 2rem; +} + +.tab-content { + display: none; +} + +.tab-content.active { + display: block; + animation: fadeIn 0.2s ease; +} + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +.tab-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 2rem; +} + +.tab-header h2 { + font-size: 1.875rem; + font-weight: 600; +} + +.header-actions { + display: flex; + gap: 1rem; + align-items: center; +} + +/* ── Forms ──────────────────────────────────────────────────────────────── */ +.form-card { + background-color: var(--surface); + border: 1px solid var(--border); + border-radius: 0.5rem; + padding: 1.5rem; + margin-bottom: 2rem; +} + +.form-card h3 { + margin-bottom: 1.5rem; + font-size: 1.25rem; +} + +.form-row { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 1.5rem; + margin-bottom: 1.5rem; +} + +.form-group { + display: flex; + flex-direction: column; +} + +.form-group.full-width { + grid-column: 1 / -1; +} + +.form-group label { + font-size: 0.875rem; + font-weight: 600; + margin-bottom: 0.5rem; + color: var(--text-muted); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.form-group input, +.form-group select { + padding: 0.75rem; + background-color: var(--surface2); + border: 1px solid var(--border); + border-radius: 0.375rem; + color: var(--text); + font-size: 0.9rem; + transition: border-color 0.2s ease; +} + +.form-group input:focus, +.form-group select:focus { + outline: none; + border-color: var(--accent); + background-color: rgba(108, 99, 255, 0.05); +} + +.form-actions { + display: flex; + gap: 1rem; + justify-content: flex-end; +} + +/* ── Buttons ────────────────────────────────────────────────────────────── */ +.btn { + padding: 0.75rem 1.5rem; + background-color: var(--surface2); + border: 1px solid var(--border); + border-radius: 0.375rem; + color: var(--text); + font-size: 0.9rem; + font-weight: 500; + cursor: pointer; + transition: all 0.2s ease; + white-space: nowrap; +} + +.btn:hover { + background-color: var(--border); + border-color: var(--text-muted); +} + +.btn:active { + transform: scale(0.98); +} + +.btn-primary { + background-color: var(--accent); + border-color: var(--accent); + color: var(--bg); +} + +.btn-primary:hover { + background-color: var(--accent-hover); + border-color: var(--accent-hover); +} + +.btn-primary:disabled { + opacity: 0.6; + cursor: not-allowed; + transform: none; +} + +.btn-danger { + background-color: var(--danger); + border-color: var(--danger); + color: white; +} + +.btn-danger:hover { + background-color: #dc2626; + border-color: #dc2626; +} + +.btn-success { + background-color: var(--success); + border-color: var(--success); + color: var(--bg); +} + +.btn-success:hover { + background-color: #16a34a; + border-color: #16a34a; +} + +.btn-sm { + padding: 0.5rem 1rem; + font-size: 0.8rem; +} + +/* ── Tables ────────────────────────────────────────────────────────────── */ +table { + width: 100%; + border-collapse: collapse; + background-color: var(--surface); + border: 1px solid var(--border); + border-radius: 0.5rem; + overflow: hidden; +} + +thead { + background-color: var(--surface2); + border-bottom: 1px solid var(--border); +} + +th { + padding: 1rem; + text-align: left; + font-weight: 600; + color: var(--text-muted); + font-size: 0.875rem; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +td { + padding: 1rem; + border-top: 1px solid var(--border); + font-size: 0.9rem; +} + +tbody tr:nth-child(odd) { + background-color: rgba(255, 255, 255, 0.01); +} + +tbody tr:hover { + background-color: rgba(108, 99, 255, 0.1); +} + +tbody tr.expiry-danger { + background-color: rgba(239, 68, 68, 0.1); + border-left: 3px solid var(--danger); +} + +tbody tr.expiry-warn { + background-color: rgba(245, 158, 11, 0.1); + border-left: 3px solid var(--warning); +} + +/* ── Cards ──────────────────────────────────────────────────────────────── */ +.card { + background-color: var(--surface); + border: 1px solid var(--border); + border-radius: 0.5rem; + padding: 1.5rem; + transition: all 0.2s ease; +} + +.card:hover { + border-color: var(--accent); + box-shadow: 0 0 0 3px rgba(108, 99, 255, 0.1); +} + +.card-title { + font-size: 1.125rem; + font-weight: 600; + margin-bottom: 0.5rem; +} + +.card-subtitle { + font-size: 0.875rem; + color: var(--text-muted); + margin-bottom: 1rem; +} + +.card-body { + font-size: 0.9rem; +} + +/* ── Menu Grid ──────────────────────────────────────────────────────────── */ +.menu-grid { + display: grid; + grid-template-columns: repeat(8, 1fr); + gap: 0.5rem; + margin-bottom: 2rem; +} + +.menu-grid-cell { + background-color: var(--surface); + border: 1px solid var(--border); + border-radius: 0.5rem; + padding: 1rem; + min-height: 120px; + display: flex; + flex-direction: column; + justify-content: space-between; + transition: all 0.2s ease; +} + +.menu-grid-cell:hover { + border-color: var(--accent); +} + +.menu-grid-cell.repeat-warning { + border: 2px solid var(--warning); +} + +.menu-grid-header { + background-color: var(--surface2); + font-weight: 600; + text-align: center; + padding: 1rem; + border-radius: 0.5rem; + border: 1px solid var(--border); +} + +.menu-meal-type { + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + color: var(--text-muted); + margin-bottom: 0.5rem; +} + +.menu-meal-name { + font-weight: 600; + margin-bottom: 0.5rem; + word-break: break-word; +} + +.menu-time { + display: inline-block; + font-size: 0.75rem; + background-color: var(--surface2); + padding: 0.25rem 0.5rem; + border-radius: 0.25rem; + margin-bottom: 0.75rem; + color: var(--text-muted); +} + +.menu-actions { + display: flex; + gap: 0.5rem; + flex-wrap: wrap; +} + +@media (max-width: 768px) { + .menu-grid { + grid-template-columns: 1fr; + } + + .menu-grid-header { + display: none; + } + + .menu-grid-cell { + position: relative; + } + + .menu-grid-cell::before { + content: attr(data-day); + position: absolute; + top: 0.5rem; + right: 0.5rem; + font-size: 0.75rem; + color: var(--text-muted); + background-color: var(--surface2); + padding: 0.25rem 0.5rem; + border-radius: 0.25rem; + } +} + +/* ── Grocery List ───────────────────────────────────────────────────────── */ +.grocery-section { + margin-bottom: 2rem; +} + +.grocery-section-title { + font-weight: 600; + font-size: 1.125rem; + margin-bottom: 1rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid var(--border); + text-transform: capitalize; +} + +.grocery-item { + display: flex; + align-items: flex-start; + gap: 1rem; + padding: 0.75rem; + background-color: rgba(255, 255, 255, 0.01); + border-radius: 0.375rem; + margin-bottom: 0.5rem; + font-size: 0.9rem; +} + +.grocery-item input[type="checkbox"] { + margin-top: 0.25rem; + cursor: pointer; + width: 18px; + height: 18px; + accent-color: var(--accent); +} + +.grocery-item-details { + flex: 1; +} + +.grocery-item-name { + font-weight: 500; + margin-bottom: 0.25rem; +} + +.grocery-item-meta { + font-size: 0.8rem; + color: var(--text-muted); +} + +.grocery-total { + background-color: var(--surface2); + border: 1px solid var(--border); + border-radius: 0.5rem; + padding: 1.5rem; + margin-bottom: 2rem; + text-align: center; +} + +.grocery-total-label { + font-size: 0.9rem; + color: var(--text-muted); + margin-bottom: 0.5rem; + text-transform: uppercase; + letter-spacing: 0.5px; + font-weight: 600; +} + +.grocery-total-amount { + font-size: 2rem; + font-weight: 700; + color: var(--accent); +} + +/* ── Stats Bar Chart ────────────────────────────────────────────────────── */ +.stats-section { + background-color: var(--surface); + border: 1px solid var(--border); + border-radius: 0.5rem; + padding: 1.5rem; + margin-bottom: 2rem; +} + +.stats-title { + font-size: 1.125rem; + font-weight: 600; + margin-bottom: 1.5rem; +} + +.stat-item { + display: grid; + grid-template-columns: 150px 1fr 80px; + gap: 1rem; + align-items: center; + margin-bottom: 1rem; +} + +.stat-label { + font-size: 0.9rem; + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.stat-bar { + display: flex; + align-items: center; + height: 28px; + background-color: var(--surface2); + border-radius: 0.375rem; + overflow: hidden; +} + +.stat-bar-fill { + background: linear-gradient(90deg, var(--accent), var(--accent-hover)); + height: 100%; + display: flex; + align-items: center; + justify-content: flex-end; + padding-right: 0.5rem; + color: var(--bg); + font-size: 0.8rem; + font-weight: 600; + transition: width 0.3s ease; +} + +.stat-count { + font-size: 0.9rem; + font-weight: 600; + text-align: right; + color: var(--text-muted); +} + +/* ── Meals Timeline ────────────────────────────────────────────────────── */ +.meals-timeline { + display: flex; + flex-direction: column; + gap: 2rem; +} + +.meal-date-group { + border-left: 3px solid var(--accent); + padding-left: 1.5rem; +} + +.meal-date-label { + font-weight: 600; + font-size: 1rem; + margin-bottom: 1rem; + color: var(--accent); +} + +.meal-entry { + background-color: var(--surface); + border: 1px solid var(--border); + border-radius: 0.5rem; + padding: 1rem; + margin-bottom: 0.75rem; + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 1rem; +} + +.meal-type-badge { + display: inline-block; + padding: 0.25rem 0.75rem; + border-radius: 0.25rem; + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + margin-bottom: 0.5rem; +} + +.meal-type-breakfast { + background-color: rgba(245, 158, 11, 0.2); + color: var(--warning); +} + +.meal-type-lunch { + background-color: rgba(108, 99, 255, 0.2); + color: var(--accent); +} + +.meal-type-dinner { + background-color: rgba(34, 197, 94, 0.2); + color: var(--success); +} + +.meal-type-snack { + background-color: rgba(59, 130, 246, 0.2); + color: var(--info); +} + +.meal-info { + flex: 1; +} + +.meal-name { + font-weight: 600; + margin-bottom: 0.25rem; +} + +.meal-time { + font-size: 0.8rem; + color: var(--text-muted); +} + +.meal-notes { + font-size: 0.85rem; + color: var(--text-muted); + margin-top: 0.5rem; + font-style: italic; +} + +/* ── Spinners ───────────────────────────────────────────────────────────── */ +.spinner { + display: inline-block; + width: 20px; + height: 20px; + border: 2px solid var(--surface2); + border-top-color: var(--accent); + border-radius: 50%; + animation: spin 0.8s linear infinite; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +/* ── Toasts ────────────────────────────────────────────────────────────── */ +#toast-container { + position: fixed; + bottom: 2rem; + right: 2rem; + z-index: 1000; + display: flex; + flex-direction: column; + gap: 1rem; +} + +.toast { + background-color: var(--surface); + border: 1px solid var(--border); + border-radius: 0.5rem; + padding: 1rem 1.5rem; + min-width: 280px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + animation: slideIn 0.3s ease; + display: flex; + gap: 0.75rem; + align-items: flex-start; +} + +@keyframes slideIn { + from { + opacity: 0; + transform: translateX(400px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +@keyframes slideOut { + to { + opacity: 0; + transform: translateX(400px); + } +} + +.toast.exiting { + animation: slideOut 0.3s ease forwards; +} + +.toast-icon { + flex-shrink: 0; + font-size: 1.25rem; + line-height: 1; +} + +.toast-message { + flex: 1; + font-size: 0.9rem; +} + +.toast-success { + border-color: var(--success); +} + +.toast-success .toast-icon { + color: var(--success); +} + +.toast-error { + border-color: var(--danger); +} + +.toast-error .toast-icon { + color: var(--danger); +} + +/* ── Utility Classes ────────────────────────────────────────────────────── */ +.hidden { + display: none !important; +} + +.empty-state { + text-align: center; + padding: 3rem 2rem; + color: var(--text-muted); + font-size: 1rem; +} + +.info-banner { + background-color: rgba(59, 130, 246, 0.1); + border: 1px solid rgba(59, 130, 246, 0.3); + border-radius: 0.5rem; + padding: 1rem; + margin-bottom: 1.5rem; + color: var(--info); + font-size: 0.9rem; +} + +.warning-banner { + background-color: rgba(245, 158, 11, 0.1); + border: 1px solid rgba(245, 158, 11, 0.3); + border-radius: 0.5rem; + padding: 1rem; + margin-bottom: 1.5rem; + color: var(--warning); + font-size: 0.9rem; +} + +/* ── Responsive ────────────────────────────────────────────────────────── */ +@media (max-width: 768px) { + main { + padding: 1rem; + } + + header { + padding: 1rem; + flex-direction: column; + gap: 1rem; + } + + .tab-nav { + padding: 0.5rem 1rem; + gap: 0.25rem; + } + + .tab-btn { + padding: 0.5rem 1rem; + font-size: 0.8rem; + } + + .tab-header { + flex-direction: column; + align-items: flex-start; + gap: 1rem; + } + + .form-row { + grid-template-columns: 1fr; + } + + .stat-item { + grid-template-columns: 120px 1fr 60px; + } + + #toast-container { + bottom: 1rem; + right: 1rem; + left: 1rem; + } + + .toast { + min-width: unset; + } +} |
