kiwi/frontend/src/theme.css
pyr0ball c166e5216a chore: initial commit — kiwi Phase 2 complete
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)
2026-03-30 22:20:48 -07:00

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;
}