Pantry tracker app with: - FastAPI backend + Vue 3 SPA frontend - SQLite via circuitforge-core (migrations 001-005) - Inventory CRUD, barcode scan, receipt OCR pipeline - Expiry prediction (deterministic + LLM fallback) - CF-core tier system integration - Cloud session support (menagerie)
536 lines
12 KiB
CSS
536 lines
12 KiB
CSS
/**
|
|
* Central Theme System for Project Thoth
|
|
*
|
|
* This file contains all reusable, theme-aware, responsive CSS classes.
|
|
* Components should use these classes instead of custom styles where possible.
|
|
*/
|
|
|
|
/* ============================================
|
|
LAYOUT UTILITIES - RESPONSIVE GRIDS
|
|
============================================ */
|
|
|
|
/* Responsive Grid - Automatically adjusts columns based on screen size */
|
|
.grid-responsive {
|
|
display: grid;
|
|
gap: var(--spacing-md);
|
|
}
|
|
|
|
/* Mobile: 1 column, Tablet: 2 columns, Desktop: 3+ columns */
|
|
.grid-auto {
|
|
display: grid;
|
|
gap: var(--spacing-md);
|
|
grid-template-columns: 1fr; /* Default to single column */
|
|
}
|
|
|
|
/* Stats grid - always fills available space */
|
|
.grid-stats {
|
|
display: grid;
|
|
gap: var(--spacing-md);
|
|
grid-template-columns: 1fr; /* Default to single column */
|
|
}
|
|
|
|
/* Force specific column counts */
|
|
.grid-1 { grid-template-columns: 1fr; }
|
|
.grid-2 { grid-template-columns: repeat(2, 1fr); }
|
|
.grid-3 { grid-template-columns: repeat(3, 1fr); }
|
|
.grid-4 { grid-template-columns: repeat(4, 1fr); }
|
|
|
|
/* ============================================
|
|
FLEXBOX UTILITIES - RESPONSIVE
|
|
============================================ */
|
|
|
|
.flex { display: flex; }
|
|
.flex-col { display: flex; flex-direction: column; }
|
|
.flex-wrap { flex-wrap: wrap; }
|
|
.flex-center {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
.flex-between {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
.flex-start {
|
|
display: flex;
|
|
justify-content: flex-start;
|
|
align-items: center;
|
|
}
|
|
.flex-end {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
align-items: center;
|
|
}
|
|
|
|
/* Stack on mobile, horizontal on desktop */
|
|
.flex-responsive {
|
|
display: flex;
|
|
gap: var(--spacing-md);
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
/* ============================================
|
|
SPACING UTILITIES
|
|
============================================ */
|
|
|
|
/* Gaps */
|
|
.gap-xs { gap: var(--spacing-xs); }
|
|
.gap-sm { gap: var(--spacing-sm); }
|
|
.gap-md { gap: var(--spacing-md); }
|
|
.gap-lg { gap: var(--spacing-lg); }
|
|
.gap-xl { gap: var(--spacing-xl); }
|
|
|
|
/* Padding */
|
|
.p-0 { padding: 0; }
|
|
.p-xs { padding: var(--spacing-xs); }
|
|
.p-sm { padding: var(--spacing-sm); }
|
|
.p-md { padding: var(--spacing-md); }
|
|
.p-lg { padding: var(--spacing-lg); }
|
|
.p-xl { padding: var(--spacing-xl); }
|
|
|
|
/* Margin */
|
|
.m-0 { margin: 0; }
|
|
.m-xs { margin: var(--spacing-xs); }
|
|
.m-sm { margin: var(--spacing-sm); }
|
|
.m-md { margin: var(--spacing-md); }
|
|
.m-lg { margin: var(--spacing-lg); }
|
|
.m-xl { margin: var(--spacing-xl); }
|
|
|
|
/* Margin/Padding specific sides */
|
|
.mt-md { margin-top: var(--spacing-md); }
|
|
.mb-md { margin-bottom: var(--spacing-md); }
|
|
.ml-md { margin-left: var(--spacing-md); }
|
|
.mr-md { margin-right: var(--spacing-md); }
|
|
|
|
.pt-md { padding-top: var(--spacing-md); }
|
|
.pb-md { padding-bottom: var(--spacing-md); }
|
|
.pl-md { padding-left: var(--spacing-md); }
|
|
.pr-md { padding-right: var(--spacing-md); }
|
|
|
|
/* ============================================
|
|
CARD COMPONENTS - THEME AWARE
|
|
============================================ */
|
|
|
|
.card {
|
|
background: var(--color-bg-card);
|
|
border-radius: var(--radius-xl);
|
|
padding: var(--spacing-xl);
|
|
box-shadow: var(--shadow-md);
|
|
transition: box-shadow 0.2s ease;
|
|
}
|
|
|
|
.card:hover {
|
|
box-shadow: var(--shadow-lg);
|
|
}
|
|
|
|
.card-sm {
|
|
background: var(--color-bg-card);
|
|
border-radius: var(--radius-lg);
|
|
padding: var(--spacing-md);
|
|
box-shadow: var(--shadow-sm);
|
|
}
|
|
|
|
.card-secondary {
|
|
background: var(--color-bg-secondary);
|
|
border-radius: var(--radius-lg);
|
|
padding: var(--spacing-lg);
|
|
box-shadow: var(--shadow-sm);
|
|
}
|
|
|
|
/* Status border variants */
|
|
.card-success { border-left: 4px solid var(--color-success); }
|
|
.card-warning { border-left: 4px solid var(--color-warning); }
|
|
.card-error { border-left: 4px solid var(--color-error); }
|
|
.card-info { border-left: 4px solid var(--color-info); }
|
|
|
|
/* ============================================
|
|
BUTTON COMPONENTS - THEME AWARE
|
|
============================================ */
|
|
|
|
.btn {
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
border: none;
|
|
border-radius: var(--radius-md);
|
|
font-size: var(--font-size-sm);
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: all 0.2s ease;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.btn:hover {
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
.btn:active {
|
|
transform: translateY(0);
|
|
}
|
|
|
|
.btn:disabled {
|
|
opacity: 0.5;
|
|
cursor: not-allowed;
|
|
transform: none;
|
|
}
|
|
|
|
/* Button variants */
|
|
.btn-primary {
|
|
background: var(--gradient-primary);
|
|
color: white;
|
|
border: none;
|
|
}
|
|
|
|
.btn-success {
|
|
background: var(--color-success);
|
|
color: white;
|
|
}
|
|
|
|
.btn-success:hover:not(:disabled) {
|
|
background: var(--color-success-dark);
|
|
}
|
|
|
|
.btn-error {
|
|
background: var(--color-error);
|
|
color: white;
|
|
}
|
|
|
|
.btn-error:hover:not(:disabled) {
|
|
background: var(--color-error-dark);
|
|
}
|
|
|
|
.btn-info {
|
|
background: var(--color-info);
|
|
color: white;
|
|
}
|
|
|
|
.btn-info:hover:not(:disabled) {
|
|
background: var(--color-info-dark);
|
|
}
|
|
|
|
.btn-secondary {
|
|
background: var(--color-bg-secondary);
|
|
color: var(--color-text-primary);
|
|
border: 2px solid var(--color-border);
|
|
}
|
|
|
|
.btn-secondary:hover:not(:disabled) {
|
|
background: var(--color-bg-primary);
|
|
border-color: var(--color-primary);
|
|
}
|
|
|
|
.btn-secondary.active {
|
|
background: var(--gradient-primary);
|
|
color: white;
|
|
border-color: var(--color-primary);
|
|
}
|
|
|
|
/* Button sizes */
|
|
.btn-sm {
|
|
padding: var(--spacing-xs) var(--spacing-sm);
|
|
font-size: var(--font-size-xs);
|
|
}
|
|
|
|
.btn-lg {
|
|
padding: var(--spacing-md) var(--spacing-xl);
|
|
font-size: var(--font-size-lg);
|
|
}
|
|
|
|
/* ============================================
|
|
FORM COMPONENTS - THEME AWARE
|
|
============================================ */
|
|
|
|
.form-group {
|
|
margin-bottom: var(--spacing-md);
|
|
}
|
|
|
|
.form-label {
|
|
display: block;
|
|
margin-bottom: var(--spacing-sm);
|
|
font-weight: 600;
|
|
color: var(--color-text-primary);
|
|
font-size: var(--font-size-sm);
|
|
}
|
|
|
|
.form-input,
|
|
.form-select,
|
|
.form-textarea {
|
|
width: 100%;
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
border: 1px solid var(--color-border);
|
|
border-radius: var(--radius-md);
|
|
background: var(--color-bg-input);
|
|
color: var(--color-text-primary);
|
|
font-size: var(--font-size-sm);
|
|
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
|
}
|
|
|
|
.form-input:focus,
|
|
.form-select:focus,
|
|
.form-textarea:focus {
|
|
outline: none;
|
|
border-color: var(--color-primary);
|
|
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
|
}
|
|
|
|
.form-textarea {
|
|
resize: vertical;
|
|
min-height: 80px;
|
|
font-family: inherit;
|
|
}
|
|
|
|
/* Form layouts */
|
|
.form-row {
|
|
display: grid;
|
|
gap: var(--spacing-md);
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
/* ============================================
|
|
TEXT UTILITIES
|
|
============================================ */
|
|
|
|
.text-xs { font-size: var(--font-size-xs); }
|
|
.text-sm { font-size: var(--font-size-sm); }
|
|
.text-base { font-size: var(--font-size-base); }
|
|
.text-lg { font-size: var(--font-size-lg); }
|
|
.text-xl { font-size: var(--font-size-xl); }
|
|
.text-2xl { font-size: var(--font-size-2xl); }
|
|
|
|
.text-primary { color: var(--color-text-primary); }
|
|
.text-secondary { color: var(--color-text-secondary); }
|
|
.text-muted { color: var(--color-text-muted); }
|
|
|
|
.text-success { color: var(--color-success); }
|
|
.text-warning { color: var(--color-warning); }
|
|
.text-error { color: var(--color-error); }
|
|
.text-info { color: var(--color-info); }
|
|
|
|
.text-center { text-align: center; }
|
|
.text-left { text-align: left; }
|
|
.text-right { text-align: right; }
|
|
|
|
.font-bold { font-weight: 700; }
|
|
.font-semibold { font-weight: 600; }
|
|
.font-normal { font-weight: 400; }
|
|
|
|
/* ============================================
|
|
RESPONSIVE UTILITIES
|
|
============================================ */
|
|
|
|
/* Show/Hide based on screen size */
|
|
.mobile-only { display: none; }
|
|
.desktop-only { display: block; }
|
|
|
|
/* Width utilities */
|
|
.w-full { width: 100%; }
|
|
.w-auto { width: auto; }
|
|
|
|
/* Height utilities */
|
|
.h-full { height: 100%; }
|
|
.h-auto { height: auto; }
|
|
|
|
/* ============================================
|
|
MOBILE BREAKPOINTS (≤480px)
|
|
============================================ */
|
|
|
|
@media (max-width: 480px) {
|
|
/* Show/Hide */
|
|
.mobile-only { display: block; }
|
|
.desktop-only { display: none; }
|
|
|
|
/* Grids already default to 1fr, just ensure it stays that way */
|
|
.grid-2,
|
|
.grid-3,
|
|
.grid-4 {
|
|
grid-template-columns: 1fr !important;
|
|
}
|
|
|
|
/* Stack flex items vertically */
|
|
.flex-responsive {
|
|
flex-direction: column;
|
|
}
|
|
|
|
/* Buttons take full width */
|
|
.btn-mobile-full {
|
|
width: 100%;
|
|
min-width: 100%;
|
|
}
|
|
|
|
/* Reduce card padding on mobile */
|
|
.card {
|
|
padding: var(--spacing-md);
|
|
}
|
|
|
|
.card-sm {
|
|
padding: var(--spacing-sm);
|
|
}
|
|
|
|
/* Allow text wrapping on mobile */
|
|
.btn {
|
|
white-space: normal;
|
|
text-align: center;
|
|
}
|
|
}
|
|
|
|
/* ============================================
|
|
TABLET BREAKPOINTS (481px - 768px)
|
|
============================================ */
|
|
|
|
@media (min-width: 481px) and (max-width: 768px) {
|
|
/* 2-column layouts on tablets */
|
|
.grid-3,
|
|
.grid-4 {
|
|
grid-template-columns: repeat(2, 1fr);
|
|
}
|
|
|
|
.grid-auto {
|
|
grid-template-columns: repeat(2, 1fr);
|
|
}
|
|
|
|
.grid-stats {
|
|
grid-template-columns: repeat(2, 1fr);
|
|
}
|
|
|
|
.form-row {
|
|
grid-template-columns: 1fr 1fr;
|
|
}
|
|
}
|
|
|
|
/* ============================================
|
|
DESKTOP BREAKPOINTS (769px - 1024px)
|
|
============================================ */
|
|
|
|
@media (min-width: 769px) and (max-width: 1024px) {
|
|
.grid-auto {
|
|
grid-template-columns: repeat(3, 1fr);
|
|
}
|
|
|
|
.grid-stats {
|
|
grid-template-columns: repeat(3, 1fr);
|
|
}
|
|
|
|
.grid-4 {
|
|
grid-template-columns: repeat(3, 1fr);
|
|
}
|
|
}
|
|
|
|
/* ============================================
|
|
LARGE DESKTOP (≥1025px)
|
|
============================================ */
|
|
|
|
@media (min-width: 1025px) {
|
|
.grid-auto {
|
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
|
}
|
|
|
|
.grid-stats {
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
}
|
|
|
|
.form-row {
|
|
grid-template-columns: 1fr 1fr;
|
|
}
|
|
}
|
|
|
|
/* ============================================
|
|
STATUS & STATE UTILITIES
|
|
============================================ */
|
|
|
|
.status-badge {
|
|
display: inline-block;
|
|
padding: var(--spacing-xs) var(--spacing-sm);
|
|
border-radius: var(--radius-sm);
|
|
font-size: var(--font-size-xs);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.status-success {
|
|
background: var(--color-success-bg);
|
|
color: var(--color-success-dark);
|
|
border: 1px solid var(--color-success-border);
|
|
}
|
|
|
|
.status-warning {
|
|
background: var(--color-warning-bg);
|
|
color: var(--color-warning-dark);
|
|
border: 1px solid var(--color-warning-border);
|
|
}
|
|
|
|
.status-error {
|
|
background: var(--color-error-bg);
|
|
color: var(--color-error-dark);
|
|
border: 1px solid var(--color-error-border);
|
|
}
|
|
|
|
.status-info {
|
|
background: var(--color-info-bg);
|
|
color: var(--color-info-dark);
|
|
border: 1px solid var(--color-info-border);
|
|
}
|
|
|
|
/* ============================================
|
|
ANIMATION UTILITIES
|
|
============================================ */
|
|
|
|
.fade-in {
|
|
animation: fadeIn 0.3s ease-in;
|
|
}
|
|
|
|
@keyframes fadeIn {
|
|
from { opacity: 0; }
|
|
to { opacity: 1; }
|
|
}
|
|
|
|
.slide-up {
|
|
animation: slideUp 0.3s ease-out;
|
|
}
|
|
|
|
@keyframes slideUp {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(20px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
/* ============================================
|
|
LOADING UTILITIES
|
|
============================================ */
|
|
|
|
.spinner {
|
|
border: 3px solid var(--color-border);
|
|
border-top: 3px solid var(--color-primary);
|
|
border-radius: 50%;
|
|
width: 40px;
|
|
height: 40px;
|
|
animation: spin 1s linear infinite;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.spinner-sm {
|
|
width: 20px;
|
|
height: 20px;
|
|
border-width: 2px;
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% { transform: rotate(0deg); }
|
|
100% { transform: rotate(360deg); }
|
|
}
|
|
|
|
/* ============================================
|
|
DIVIDER
|
|
============================================ */
|
|
|
|
.divider {
|
|
height: 1px;
|
|
background: var(--color-border);
|
|
margin: var(--spacing-lg) 0;
|
|
}
|
|
|
|
.divider-md {
|
|
margin: var(--spacing-md) 0;
|
|
}
|