Files
smart-city-digital-twin-mar…/smart-app-city/design/shared.css
Eric FELIXINE e30ae8ed09 feat(smart-app): implement complete mobile app MVP
- App.tsx: full navigation (Auth stack + Main tabs with 5 screens)
- Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen
- HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors
- MapScreen: interactive map with layer toggles (6 layers)
- MarketplaceScreen: categories (6), products (5), search
- ChatScreen: AI chat with quick prompts (4), bot responses
- ProfileScreen: user info, stats, menu (9 items), logout
- AlertsScreen: alert list with severity, acknowledge
- SensorsScreen: sensor list with type filters (6 types), search
- ZonesScreen: zone cards with stats
- SettingsScreen: language picker (FR/EN/ES/DE), privacy, about
- Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n
- Hooks: useSensors, useAlerts, useNotifications, useLocation
- Components: Card, Button, LoadingSpinner, ErrorBoundary, Header
- Services: iotService, notificationService (with axios API client)
- Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN)
- Theme: colors.ts with full design system (Blue Ocean palette)
- Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
2026-06-01 18:00:35 -04:00

431 lines
11 KiB
CSS

/* Smart App City — Design System */
/* i18n: FR / EN / ES / DE */
/* Platform: React Native → mobile-first */
:root {
/* ── Primary Palette ── */
--primary-50: #E8F5E9;
--primary-100: #C8E6C9;
--primary-200: #A5D6A7;
--primary-300: #81C784;
--primary-400: #66BB6A;
--primary-500: #2E7D32;
--primary-600: #2B6B2E;
--primary-700: #1B5E20;
/* ── Ocean Accent (Martinique theme) ── */
--ocean-50: #E0F7FA;
--ocean-100: #B2EBF2;
--ocean-200: #80DEEA;
--ocean-300: #4DD0E1;
--ocean-400: #26C6DA;
--ocean-500: #00ACC1;
--ocean-600: #00838F;
/* ── Alert Colors ── */
--alert-danger: #D32F2F;
--alert-warning: #F57C00;
--alert-success: #2E7D32;
--alert-info: #0288D1;
/* ── Neutrals ── */
--neutral-0: #FFFFFF;
--neutral-50: #FAFAFA;
--neutral-100: #F5F5F5;
--neutral-200: #EEEEEE;
--neutral-300: #E0E0E0;
--neutral-400: #BDBDBD;
--neutral-500: #9E9E9E;
--neutral-600: #757575;
--neutral-700: #616161;
--neutral-800: #424242;
--neutral-900: #212121;
--neutral-1000: #1A1A1A;
/* ── Typography ── */
--font-family: 'Inter', 'SF Pro Display', -apple-system, BlinkMacSystemFont, sans-serif;
--font-family-mono: 'JetBrains Mono', 'SF Mono', monospace;
--text-xs: 11px;
--text-sm: 13px;
--text-base: 15px;
--text-md: 17px;
--text-lg: 20px;
--text-xl: 24px;
--text-2xl: 28px;
--text-3xl: 34px;
--text-4xl: 40px;
--weight-regular: 400;
--weight-medium: 500;
--weight-semibold: 600;
--weight-bold: 700;
/* ── Spacing ── */
--space-2xs: 2px;
--space-xs: 4px;
--space-sm: 8px;
--space-md: 12px;
--space-base: 16px;
--space-lg: 20px;
--space-xl: 24px;
--space-2xl: 32px;
--space-3xl: 40px;
--space-4xl: 48px;
/* ── Border Radius ── */
--radius-sm: 8px;
--radius-md: 12px;
--radius-lg: 16px;
--radius-xl: 20px;
--radius-2xl: 24px;
--radius-full: 9999px;
/* ── Shadows ── */
--shadow-sm: 0 1px 3px rgba(0,0,0,0.08), 0 1px 2px rgba(0,0,0,0.04);
--shadow-md: 0 4px 12px rgba(0,0,0,0.1), 0 2px 4px rgba(0,0,0,0.06);
--shadow-lg: 0 8px 24px rgba(0,0,0,0.12), 0 4px 8px rgba(0,0,0,0.06);
--shadow-xl: 0 16px 48px rgba(0,0,0,0.16);
/* ── Dark Mode ── */
--bg-primary-dark: #121212;
--bg-secondary-dark: #1E1E1E;
--bg-card-dark: #2A2A2A;
--text-primary-dark: #ECECEC;
--text-secondary-dark: #A0A0A0;
--text-tertiary-dark: #6A6A6A;
}
/* ── Base Reset ── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: var(--font-family);
font-size: var(--text-base);
color: var(--neutral-900);
background: var(--neutral-50);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
}
body.dark {
background: var(--bg-primary-dark);
color: var(--text-primary-dark);
}
/* ── Utility Classes ── */
.flex { display: flex; }
.flex-col { flex-direction: column; }
.items-center { align-items: center; }
.justify-center { justify-content: center; }
.justify-between { justify-content: space-between; }
.gap-xs { gap: var(--space-xs); }
.gap-sm { gap: var(--space-sm); }
.gap-md { gap: var(--space-md); }
.gap-base { gap: var(--space-base); }
.gap-lg { gap: var(--space-lg); }
.text-center { text-align: center; }
.w-full { width: 100%; }
.h-full { height: 100%; }
.relative { position: relative; }
.absolute { position: absolute; }
.rounded-md { border-radius: var(--radius-md); }
.rounded-lg { border-radius: var(--radius-lg); }
.rounded-full { border-radius: var(--radius-full); }
.shadow-sm { box-shadow: var(--shadow-sm); }
.shadow-md { box-shadow: var(--shadow-md); }
.shadow-lg { box-shadow: var(--shadow-lg); }
/* ── Component: Mobile Frame ── */
.mobile-frame {
width: 390px;
height: 844px;
border-radius: 40px;
border: 3px solid var(--neutral-800);
overflow: hidden;
position: relative;
background: var(--neutral-0);
box-shadow: var(--shadow-xl), 0 0 0 1px rgba(0,0,0,0.04);
}
.dark .mobile-frame {
border-color: var(--neutral-600);
background: var(--bg-primary-dark);
}
/* ── Notch ── */
.notch {
width: 120px;
height: 30px;
background: var(--neutral-900);
border-radius: 0 0 20px 20px;
position: absolute;
top: 0;
left: 50%;
transform: translateX(-50%);
z-index: 100;
}
.dark .notch { background: var(--bg-primary-dark); border: 2px solid var(--neutral-600); border-top: none; }
/* ── Status Bar ── */
.status-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 24px 4px;
font-size: var(--text-sm);
font-weight: var(--weight-semibold);
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 50;
}
/* ── Bottom Nav ── */
.bottom-nav {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 80px;
background: var(--neutral-0);
border-top: 1px solid var(--neutral-200);
display: flex;
justify-content: space-around;
align-items: center;
padding-bottom: 20px;
z-index: 50;
}
.dark .bottom-nav {
background: var(--bg-secondary-dark);
border-color: var(--neutral-700);
}
.bottom-nav-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
font-size: var(--text-xs);
color: var(--neutral-500);
cursor: pointer;
transition: color 0.2s;
}
.bottom-nav-item.active { color: var(--primary-500); }
.bottom-nav-item svg { width: 24px; height: 24px; }
/* ── Content Area ── */
.content-area {
position: absolute;
top: 44px;
bottom: 80px;
left: 0;
right: 0;
overflow-y: auto;
overflow-x: hidden;
scrollbar-width: none;
}
.content-area::-webkit-scrollbar { display: none; }
/* ── Cards ── */
.card {
background: var(--neutral-0);
border-radius: var(--radius-lg);
padding: var(--space-base);
box-shadow: var(--shadow-sm);
border: 1px solid var(--neutral-100);
}
.dark .card {
background: var(--bg-card-dark);
border-color: var(--neutral-700);
}
/* ── Buttons ── */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--space-xs);
padding: 12px 24px;
border-radius: var(--radius-full);
font-weight: var(--weight-semibold);
font-size: var(--text-base);
cursor: pointer;
border: none;
transition: all 0.2s;
min-height: 48px;
}
.btn-primary {
background: var(--primary-500);
color: white;
}
.btn-primary:hover { background: var(--primary-600); }
.btn-secondary {
background: var(--neutral-200);
color: var(--neutral-800);
}
.btn-ghost {
background: transparent;
color: var(--primary-500);
}
.btn-block { width: 100%; }
/* ── Metric Cards ── */
.metric-card {
padding: var(--space-base);
border-radius: var(--radius-lg);
background: var(--neutral-0);
border: 1px solid var(--neutral-200);
text-align: center;
}
.dark .metric-card {
background: var(--bg-card-dark);
border-color: var(--neutral-700);
}
.metric-value {
font-size: var(--text-2xl);
font-weight: var(--weight-bold);
color: var(--primary-500);
}
.metric-label {
font-size: var(--text-xs);
color: var(--neutral-500);
margin-top: 4px;
}
/* ── Label ── */
.label {
font-size: var(--text-sm);
font-weight: var(--weight-medium);
color: var(--neutral-600);
margin-bottom: var(--space-xs);
}
.dark .label { color: var(--text-secondary-dark); }
/* ── Page Title ── */
.page-title {
font-size: var(--text-lg);
font-weight: var(--weight-bold);
color: var(--neutral-900);
}
.dark .page-title { color: var(--text-primary-dark); }
/* ── Section Header ── */
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--space-base);
}
.section-title {
font-size: var(--text-md);
font-weight: var(--weight-semibold);
}
/* ── Online Indicator ── */
.online-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--alert-success);
display: inline-block;
}
/* ── Badge ── */
.badge {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 20px;
height: 20px;
border-radius: var(--radius-full);
background: var(--alert-danger);
color: white;
font-size: var(--text-xs);
font-weight: var(--weight-bold);
padding: 0 6px;
}
/* ── Chip ── */
.chip {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 4px 12px;
border-radius: var(--radius-full);
background: var(--primary-50);
color: var(--primary-600);
font-size: var(--text-sm);
font-weight: var(--weight-medium);
}
.dark .chip { background: rgba(46,125,50,0.15); }
/* ── Gradient Overlay ── */
.gradient-green {
background: linear-gradient(135deg, var(--primary-500), var(--ocean-500));
}
.gradient-ocean {
background: linear-gradient(135deg, var(--ocean-400), var(--ocean-600));
}
/* ── SVG Icon Base ── */
.icon { width: 24px; height: 24px; stroke-width: 2; fill: none; stroke: currentColor; }
.icon-sm { width: 18px; height: 18px; }
.icon-lg { width: 32px; height: 32px; }
.icon-xl { width: 48px; height: 48px; }
/* ── Thumbnail / Avatar ── */
.avatar {
width: 40px;
height: 40px;
border-radius: var(--radius-full);
object-fit: cover;
background: var(--neutral-200);
}
.avatar-sm { width: 32px; height: 32px; }
.avatar-lg { width: 56px; height: 56px; }
/* ── Spacing Utilities ── */
.px-base { padding-left: var(--space-base); padding-right: var(--space-base); }
.py-sm { padding-top: var(--space-sm); padding-bottom: var(--space-sm); }
.py-base { padding-top: var(--space-base); padding-bottom: var(--space-base); }
.p-base { padding: var(--space-base); }
.p-lg { padding: var(--space-lg); }
.mt-sm { margin-top: var(--space-sm); }
.mt-base { margin-top: var(--space-base); }
.mt-lg { margin-top: var(--space-lg); }
.mb-sm { margin-bottom: var(--space-sm); }
.mb-base { margin-bottom: var(--space-base); }
/* ── Grid ── */
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-md); }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-sm); }
.grid-auto { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: var(--space-md); }
/* ── Scrollable Row ── */
.scroll-row {
display: flex;
gap: var(--space-md);
overflow-x: auto;
padding: 0 var(--space-base) var(--space-base);
scrollbar-width: none;
}
.scroll-row::-webkit-scrollbar { display: none; }
/* ── Language Picker ── */
.lang-btn {
padding: 6px 12px;
border-radius: var(--radius-md);
border: 2px solid var(--neutral-300);
background: transparent;
font-size: var(--text-sm);
font-weight: var(--weight-semibold);
cursor: pointer;
color: var(--neutral-700);
transition: all 0.2s;
}
.lang-btn.active {
border-color: var(--primary-500);
background: var(--primary-500);
color: white;
}