Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
@@ -0,0 +1,610 @@
|
||||
# Admin Design System
|
||||
|
||||
> Comprehensive design system for administrative interfaces with modern UI patterns, accessibility standards, and developer efficiency.
|
||||
|
||||
## 🎯 Design Principles
|
||||
|
||||
### User Experience
|
||||
- **Clarity First** - Information hierarchy and visual clarity over decoration
|
||||
- **Efficiency** - Minimize cognitive load and interaction friction
|
||||
- **Consistency** - Predictable patterns and behaviors across all interfaces
|
||||
- **Accessibility** - WCAG 2.1 AA compliance and inclusive design
|
||||
|
||||
### Technical Excellence
|
||||
- **Performance** - Fast load times and smooth interactions
|
||||
- **Maintainability** - Modular, reusable components
|
||||
- **Scalability** - Adaptable to growing feature requirements
|
||||
- **Developer Experience** - Clear documentation and easy implementation
|
||||
|
||||
## 🎨 Visual Language
|
||||
|
||||
### Color System
|
||||
|
||||
```css
|
||||
/* Primary Admin Palette */
|
||||
--admin-primary: #6366f1; /* Indigo - Primary actions */
|
||||
--admin-primary-hover: #5855eb; /* Hover states */
|
||||
--admin-primary-active: #4f46e5; /* Active states */
|
||||
--admin-primary-light: #a5b4fc; /* Backgrounds */
|
||||
|
||||
/* Semantic Colors */
|
||||
--admin-success: #10b981; /* Success states */
|
||||
--admin-warning: #f59e0b; /* Warning states */
|
||||
--admin-error: #ef4444; /* Error states */
|
||||
--admin-info: #3b82f6; /* Information */
|
||||
|
||||
/* Neutral System */
|
||||
--admin-gray-50: #f8fafc; /* Lightest backgrounds */
|
||||
--admin-gray-100: #f1f5f9; /* Light backgrounds */
|
||||
--admin-gray-200: #e2e8f0; /* Borders */
|
||||
--admin-gray-300: #cbd5e1; /* Disabled states */
|
||||
--admin-gray-400: #94a3b8; /* Placeholders */
|
||||
--admin-gray-500: #64748b; /* Secondary text */
|
||||
--admin-gray-600: #475569; /* Primary text */
|
||||
--admin-gray-700: #334155; /* Headings */
|
||||
--admin-gray-800: #1e293b; /* Dark text */
|
||||
--admin-gray-900: #0f172a; /* Darkest elements */
|
||||
|
||||
/* Dark Theme */
|
||||
--admin-dark-bg: #0f172a;
|
||||
--admin-dark-surface: #1e293b;
|
||||
--admin-dark-border: #334155;
|
||||
--admin-dark-text: #f1f5f9;
|
||||
```
|
||||
|
||||
### Typography
|
||||
|
||||
```css
|
||||
/* Font Stacks */
|
||||
--admin-font-sans: 'Inter', system-ui, -apple-system, sans-serif;
|
||||
--admin-font-mono: 'SF Mono', 'Monaco', 'Cascadia Code', monospace;
|
||||
|
||||
/* Type Scale */
|
||||
--admin-text-xs: 0.75rem; /* 12px - Small labels */
|
||||
--admin-text-sm: 0.875rem; /* 14px - Body text */
|
||||
--admin-text-base: 1rem; /* 16px - Default */
|
||||
--admin-text-lg: 1.125rem; /* 18px - Large body */
|
||||
--admin-text-xl: 1.25rem; /* 20px - Small headings */
|
||||
--admin-text-2xl: 1.5rem; /* 24px - Section headings */
|
||||
--admin-text-3xl: 1.875rem; /* 30px - Page titles */
|
||||
--admin-text-4xl: 2.25rem; /* 36px - Large titles */
|
||||
|
||||
/* Font Weights */
|
||||
--admin-font-light: 300;
|
||||
--admin-font-normal: 400;
|
||||
--admin-font-medium: 500;
|
||||
--admin-font-semibold: 600;
|
||||
--admin-font-bold: 700;
|
||||
```
|
||||
|
||||
### Spacing System
|
||||
|
||||
```css
|
||||
/* 4px base unit scaling */
|
||||
--admin-space-0: 0;
|
||||
--admin-space-1: 0.25rem; /* 4px */
|
||||
--admin-space-2: 0.5rem; /* 8px */
|
||||
--admin-space-3: 0.75rem; /* 12px */
|
||||
--admin-space-4: 1rem; /* 16px */
|
||||
--admin-space-5: 1.25rem; /* 20px */
|
||||
--admin-space-6: 1.5rem; /* 24px */
|
||||
--admin-space-8: 2rem; /* 32px */
|
||||
--admin-space-10: 2.5rem; /* 40px */
|
||||
--admin-space-12: 3rem; /* 48px */
|
||||
--admin-space-16: 4rem; /* 64px */
|
||||
--admin-space-20: 5rem; /* 80px */
|
||||
--admin-space-24: 6rem; /* 96px */
|
||||
```
|
||||
|
||||
### Elevation & Shadows
|
||||
|
||||
```css
|
||||
/* Shadow System */
|
||||
--admin-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||
--admin-shadow-base: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
|
||||
--admin-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||
--admin-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
--admin-shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||
|
||||
/* Border Radius */
|
||||
--admin-radius-none: 0;
|
||||
--admin-radius-sm: 0.125rem; /* 2px */
|
||||
--admin-radius-base: 0.25rem; /* 4px */
|
||||
--admin-radius-md: 0.375rem; /* 6px */
|
||||
--admin-radius-lg: 0.5rem; /* 8px */
|
||||
--admin-radius-xl: 0.75rem; /* 12px */
|
||||
--admin-radius-2xl: 1rem; /* 16px */
|
||||
--admin-radius-full: 9999px; /* Pill shape */
|
||||
```
|
||||
|
||||
## 🏗 Layout System
|
||||
|
||||
### Grid Foundation
|
||||
|
||||
```css
|
||||
/* Admin Layout Grid */
|
||||
.admin-layout {
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
"header header"
|
||||
"nav main"
|
||||
"nav footer";
|
||||
grid-template-columns: 250px 1fr;
|
||||
grid-template-rows: auto 1fr auto;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.admin-header { grid-area: header; }
|
||||
.admin-nav { grid-area: nav; }
|
||||
.admin-main { grid-area: main; }
|
||||
.admin-footer { grid-area: footer; }
|
||||
|
||||
/* Responsive Adaptations */
|
||||
@media (max-width: 768px) {
|
||||
.admin-layout {
|
||||
grid-template-areas:
|
||||
"header"
|
||||
"main"
|
||||
"footer";
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.admin-nav {
|
||||
transform: translateX(-100%);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.admin-nav.is-open {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Content Layout
|
||||
|
||||
```css
|
||||
/* Page Structure */
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: var(--admin-space-6) 0;
|
||||
border-bottom: 1px solid var(--admin-gray-200);
|
||||
margin-bottom: var(--admin-space-6);
|
||||
}
|
||||
|
||||
.page-content {
|
||||
max-width: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Content Sections */
|
||||
.admin-section {
|
||||
background: var(--admin-surface);
|
||||
border: 1px solid var(--admin-border);
|
||||
border-radius: var(--admin-radius-lg);
|
||||
padding: var(--admin-space-6);
|
||||
margin-bottom: var(--admin-space-6);
|
||||
}
|
||||
|
||||
.admin-section + .admin-section {
|
||||
margin-top: var(--admin-space-8);
|
||||
}
|
||||
```
|
||||
|
||||
## 🧩 Component Architecture
|
||||
|
||||
### Base Component Classes
|
||||
|
||||
```css
|
||||
/* Button System */
|
||||
.admin-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--admin-space-2);
|
||||
padding: var(--admin-space-2) var(--admin-space-4);
|
||||
border: 1px solid transparent;
|
||||
border-radius: var(--admin-radius-md);
|
||||
font-family: var(--admin-font-sans);
|
||||
font-size: var(--admin-text-sm);
|
||||
font-weight: var(--admin-font-medium);
|
||||
line-height: 1.5;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
|
||||
&:focus {
|
||||
outline: 2px solid var(--admin-primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
/* Button Variants */
|
||||
.admin-button--primary {
|
||||
background: var(--admin-primary);
|
||||
color: white;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background: var(--admin-primary-hover);
|
||||
}
|
||||
}
|
||||
|
||||
.admin-button--secondary {
|
||||
background: var(--admin-gray-100);
|
||||
color: var(--admin-gray-700);
|
||||
border-color: var(--admin-gray-200);
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background: var(--admin-gray-200);
|
||||
}
|
||||
}
|
||||
|
||||
.admin-button--ghost {
|
||||
background: transparent;
|
||||
color: var(--admin-gray-600);
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background: var(--admin-gray-100);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Form Components
|
||||
|
||||
```css
|
||||
/* Input System */
|
||||
.admin-input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: var(--admin-space-3);
|
||||
border: 1px solid var(--admin-gray-300);
|
||||
border-radius: var(--admin-radius-md);
|
||||
font-family: var(--admin-font-sans);
|
||||
font-size: var(--admin-text-sm);
|
||||
line-height: 1.5;
|
||||
background: white;
|
||||
transition: all 0.15s ease;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: var(--admin-primary);
|
||||
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background: var(--admin-gray-50);
|
||||
color: var(--admin-gray-400);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
&.is-error {
|
||||
border-color: var(--admin-error);
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Form Groups */
|
||||
.admin-form-group {
|
||||
margin-bottom: var(--admin-space-4);
|
||||
}
|
||||
|
||||
.admin-label {
|
||||
display: block;
|
||||
margin-bottom: var(--admin-space-2);
|
||||
font-size: var(--admin-text-sm);
|
||||
font-weight: var(--admin-font-medium);
|
||||
color: var(--admin-gray-700);
|
||||
}
|
||||
|
||||
.admin-help-text {
|
||||
margin-top: var(--admin-space-1);
|
||||
font-size: var(--admin-text-xs);
|
||||
color: var(--admin-gray-500);
|
||||
}
|
||||
|
||||
.admin-error-text {
|
||||
margin-top: var(--admin-space-1);
|
||||
font-size: var(--admin-text-xs);
|
||||
color: var(--admin-error);
|
||||
}
|
||||
```
|
||||
|
||||
### Data Display Components
|
||||
|
||||
```css
|
||||
/* Stats Cards */
|
||||
.admin-stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
gap: var(--admin-space-6);
|
||||
margin-bottom: var(--admin-space-8);
|
||||
}
|
||||
|
||||
.admin-stat-card {
|
||||
background: var(--admin-surface);
|
||||
border: 1px solid var(--admin-border);
|
||||
border-radius: var(--admin-radius-lg);
|
||||
padding: var(--admin-space-6);
|
||||
|
||||
&:hover {
|
||||
border-color: var(--admin-primary-light);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--admin-shadow-md);
|
||||
}
|
||||
}
|
||||
|
||||
.admin-stat-label {
|
||||
display: block;
|
||||
font-size: var(--admin-text-sm);
|
||||
font-weight: var(--admin-font-medium);
|
||||
color: var(--admin-gray-600);
|
||||
margin-bottom: var(--admin-space-2);
|
||||
}
|
||||
|
||||
.admin-stat-value {
|
||||
font-size: var(--admin-text-3xl);
|
||||
font-weight: var(--admin-font-bold);
|
||||
color: var(--admin-gray-900);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.admin-stat-trend {
|
||||
margin-top: var(--admin-space-2);
|
||||
font-size: var(--admin-text-xs);
|
||||
|
||||
&.is-positive { color: var(--admin-success); }
|
||||
&.is-negative { color: var(--admin-error); }
|
||||
&.is-neutral { color: var(--admin-gray-500); }
|
||||
}
|
||||
```
|
||||
|
||||
### Tables
|
||||
|
||||
```css
|
||||
/* Admin Tables */
|
||||
.admin-table {
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
background: var(--admin-surface);
|
||||
border: 1px solid var(--admin-border);
|
||||
border-radius: var(--admin-radius-lg);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.admin-table th {
|
||||
background: var(--admin-gray-50);
|
||||
padding: var(--admin-space-4);
|
||||
text-align: left;
|
||||
font-size: var(--admin-text-xs);
|
||||
font-weight: var(--admin-font-semibold);
|
||||
color: var(--admin-gray-600);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
border-bottom: 1px solid var(--admin-border);
|
||||
|
||||
&[data-sort] {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
|
||||
&:hover {
|
||||
background: var(--admin-gray-100);
|
||||
}
|
||||
|
||||
&.is-sorted-asc::after {
|
||||
content: " ↑";
|
||||
color: var(--admin-primary);
|
||||
}
|
||||
|
||||
&.is-sorted-desc::after {
|
||||
content: " ↓";
|
||||
color: var(--admin-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.admin-table td {
|
||||
padding: var(--admin-space-4);
|
||||
border-bottom: 1px solid var(--admin-gray-100);
|
||||
font-size: var(--admin-text-sm);
|
||||
color: var(--admin-gray-700);
|
||||
}
|
||||
|
||||
.admin-table tbody tr:hover {
|
||||
background: var(--admin-gray-50);
|
||||
}
|
||||
|
||||
.admin-table tbody tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
```
|
||||
|
||||
## 🌓 Dark Mode Support
|
||||
|
||||
```css
|
||||
/* Dark Theme Variables */
|
||||
[data-theme="dark"] {
|
||||
--admin-surface: var(--admin-dark-surface);
|
||||
--admin-border: var(--admin-dark-border);
|
||||
--admin-text: var(--admin-dark-text);
|
||||
--admin-bg: var(--admin-dark-bg);
|
||||
|
||||
/* Component Adaptations */
|
||||
--admin-gray-50: #334155;
|
||||
--admin-gray-100: #475569;
|
||||
--admin-gray-200: #64748b;
|
||||
--admin-gray-600: #cbd5e1;
|
||||
--admin-gray-700: #e2e8f0;
|
||||
--admin-gray-900: var(--admin-dark-text);
|
||||
}
|
||||
|
||||
/* Theme Toggle Component */
|
||||
.admin-theme-toggle {
|
||||
position: relative;
|
||||
width: 48px;
|
||||
height: 24px;
|
||||
background: var(--admin-gray-200);
|
||||
border: none;
|
||||
border-radius: var(--admin-radius-full);
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: white;
|
||||
border-radius: 50%;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
&.is-dark {
|
||||
background: var(--admin-primary);
|
||||
|
||||
&::after {
|
||||
transform: translateX(24px);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ♿ Accessibility Standards
|
||||
|
||||
### Focus Management
|
||||
|
||||
```css
|
||||
/* Focus Styles */
|
||||
.admin-focus-visible {
|
||||
outline: 2px solid var(--admin-primary);
|
||||
outline-offset: 2px;
|
||||
border-radius: var(--admin-radius-sm);
|
||||
}
|
||||
|
||||
/* Skip Links */
|
||||
.admin-skip-link {
|
||||
position: absolute;
|
||||
top: -40px;
|
||||
left: 6px;
|
||||
background: var(--admin-primary);
|
||||
color: white;
|
||||
padding: 8px;
|
||||
text-decoration: none;
|
||||
border-radius: var(--admin-radius-md);
|
||||
z-index: 1000;
|
||||
|
||||
&:focus {
|
||||
top: 6px;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ARIA Support
|
||||
|
||||
```html
|
||||
<!-- Navigation with proper ARIA -->
|
||||
<nav class="admin-nav" role="navigation" aria-label="Main navigation">
|
||||
<ul role="menubar">
|
||||
<li role="none">
|
||||
<a href="/admin" role="menuitem" aria-current="page">Dashboard</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<!-- Data tables with proper headers -->
|
||||
<table class="admin-table" role="table" aria-label="User data">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th role="columnheader" aria-sort="ascending">Name</th>
|
||||
<th role="columnheader">Email</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody role="rowgroup">
|
||||
<tr role="row">
|
||||
<td role="gridcell">John Doe</td>
|
||||
<td role="gridcell">john@example.com</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
```
|
||||
|
||||
## 📱 Responsive Behavior
|
||||
|
||||
### Breakpoint System
|
||||
|
||||
```css
|
||||
/* Mobile First Breakpoints */
|
||||
.admin-responsive {
|
||||
/* Mobile (default) */
|
||||
padding: var(--admin-space-4);
|
||||
|
||||
/* Tablet */
|
||||
@media (min-width: 768px) {
|
||||
padding: var(--admin-space-6);
|
||||
}
|
||||
|
||||
/* Desktop */
|
||||
@media (min-width: 1024px) {
|
||||
padding: var(--admin-space-8);
|
||||
}
|
||||
|
||||
/* Large Desktop */
|
||||
@media (min-width: 1280px) {
|
||||
padding: var(--admin-space-12);
|
||||
}
|
||||
}
|
||||
|
||||
/* Navigation Adaptations */
|
||||
@media (max-width: 767px) {
|
||||
.admin-nav {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 280px;
|
||||
height: 100vh;
|
||||
background: var(--admin-surface);
|
||||
border-right: 1px solid var(--admin-border);
|
||||
transform: translateX(-100%);
|
||||
transition: transform 0.3s ease;
|
||||
z-index: 1000;
|
||||
|
||||
&.is-open {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.admin-nav-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: all 0.3s ease;
|
||||
z-index: 999;
|
||||
|
||||
&.is-active {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*For implementation examples, see [Admin Components](components.md)*
|
||||
*For JavaScript integration, see [JavaScript Modules](../javascript.md)*
|
||||
*For performance guidelines, see [CSS Architecture](../css-architecture.md)*
|
||||
471
backups/docs-backup-20250731125004/design-system/components.md
Normal file
471
backups/docs-backup-20250731125004/design-system/components.md
Normal file
@@ -0,0 +1,471 @@
|
||||
# UI Components
|
||||
|
||||
> Component library documentation and usage examples for the design system.
|
||||
|
||||
## 🧩 Component Architecture
|
||||
|
||||
All components follow a consistent structure with design tokens, accessibility standards, and responsive behavior.
|
||||
|
||||
### Base Component Pattern
|
||||
|
||||
```html
|
||||
<div class="component-name" data-component="name" role="[role]" aria-label="[description]">
|
||||
<div class="component-name__element">
|
||||
<!-- Component content -->
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Component CSS Structure
|
||||
|
||||
```css
|
||||
/* Block */
|
||||
.component-name {
|
||||
/* Base styles using design tokens */
|
||||
}
|
||||
|
||||
/* Element */
|
||||
.component-name__element {
|
||||
/* Element styles */
|
||||
}
|
||||
|
||||
/* Modifier */
|
||||
.component-name--variant {
|
||||
/* Variant styles */
|
||||
}
|
||||
|
||||
/* State */
|
||||
.component-name.is-active {
|
||||
/* State styles */
|
||||
}
|
||||
```
|
||||
|
||||
## 📋 Form Components
|
||||
|
||||
### Buttons
|
||||
|
||||
Basic button implementation with variants:
|
||||
|
||||
```html
|
||||
<!-- Primary Button -->
|
||||
<button class="btn btn--primary" type="button">
|
||||
Primary Action
|
||||
</button>
|
||||
|
||||
<!-- Secondary Button -->
|
||||
<button class="btn btn--secondary" type="button">
|
||||
Secondary Action
|
||||
</button>
|
||||
|
||||
<!-- Ghost Button -->
|
||||
<button class="btn btn--ghost" type="button">
|
||||
Ghost Action
|
||||
</button>
|
||||
```
|
||||
|
||||
**CSS Implementation:**
|
||||
```css
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
padding: var(--space-3) var(--space-4);
|
||||
border: 1px solid transparent;
|
||||
border-radius: var(--radius-md);
|
||||
font-family: var(--font-family-sans);
|
||||
font-size: var(--text-sm);
|
||||
font-weight: var(--font-medium);
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: all var(--duration-150) var(--ease-out);
|
||||
|
||||
&:focus-visible {
|
||||
outline: 2px solid var(--color-primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.btn--primary {
|
||||
background: var(--color-primary);
|
||||
color: white;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background: var(--color-primary-600);
|
||||
}
|
||||
}
|
||||
|
||||
.btn--secondary {
|
||||
background: var(--color-gray-100);
|
||||
color: var(--color-gray-700);
|
||||
border-color: var(--color-gray-200);
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background: var(--color-gray-200);
|
||||
}
|
||||
}
|
||||
|
||||
.btn--ghost {
|
||||
background: transparent;
|
||||
color: var(--color-gray-600);
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background: var(--color-gray-100);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Input Fields
|
||||
|
||||
Form input components with validation states:
|
||||
|
||||
```html
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="example-input">
|
||||
Field Label
|
||||
</label>
|
||||
<input
|
||||
class="form-input"
|
||||
type="text"
|
||||
id="example-input"
|
||||
placeholder="Enter text..."
|
||||
aria-describedby="example-help"
|
||||
/>
|
||||
<div class="form-help" id="example-help">
|
||||
Optional help text for this field
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Error State -->
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="error-input">
|
||||
Field with Error
|
||||
</label>
|
||||
<input
|
||||
class="form-input form-input--error"
|
||||
type="text"
|
||||
id="error-input"
|
||||
aria-invalid="true"
|
||||
aria-describedby="error-message"
|
||||
/>
|
||||
<div class="form-error" id="error-message">
|
||||
This field is required
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## 🎭 Layout Components
|
||||
|
||||
### Cards
|
||||
|
||||
Content containers with consistent styling:
|
||||
|
||||
```html
|
||||
<div class="card">
|
||||
<div class="card__header">
|
||||
<h3 class="card__title">Card Title</h3>
|
||||
<div class="card__actions">
|
||||
<button class="btn btn--ghost">Action</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card__body">
|
||||
<p>Card content goes here...</p>
|
||||
</div>
|
||||
<div class="card__footer">
|
||||
<small class="text-muted">Footer information</small>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Modals
|
||||
|
||||
Accessible modal dialogs:
|
||||
|
||||
```html
|
||||
<div class="modal" role="dialog" aria-modal="true" aria-labelledby="modal-title">
|
||||
<div class="modal__overlay" data-modal-close></div>
|
||||
<div class="modal__content">
|
||||
<div class="modal__header">
|
||||
<h2 class="modal__title" id="modal-title">Modal Title</h2>
|
||||
<button class="modal__close" data-modal-close aria-label="Close modal">
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal__body">
|
||||
<p>Modal content...</p>
|
||||
</div>
|
||||
<div class="modal__footer">
|
||||
<button class="btn btn--primary">Confirm</button>
|
||||
<button class="btn btn--secondary" data-modal-close>Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## 📊 Data Display
|
||||
|
||||
### Tables
|
||||
|
||||
Data tables with sorting and filtering:
|
||||
|
||||
```html
|
||||
<div class="table-container">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-sort="name" class="table__header--sortable">
|
||||
Name
|
||||
<span class="table__sort-indicator"></span>
|
||||
</th>
|
||||
<th data-sort="email" class="table__header--sortable">
|
||||
Email
|
||||
<span class="table__sort-indicator"></span>
|
||||
</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>John Doe</td>
|
||||
<td>john@example.com</td>
|
||||
<td>
|
||||
<div class="table__actions">
|
||||
<button class="btn btn--ghost btn--sm">Edit</button>
|
||||
<button class="btn btn--ghost btn--sm">Delete</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Stats Cards
|
||||
|
||||
Dashboard statistics display:
|
||||
|
||||
```html
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<div class="stat-card__icon">
|
||||
📊
|
||||
</div>
|
||||
<div class="stat-card__content">
|
||||
<div class="stat-card__label">Total Users</div>
|
||||
<div class="stat-card__value">1,234</div>
|
||||
<div class="stat-card__trend stat-card__trend--positive">
|
||||
↗ +12% from last month
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## 🔔 Feedback Components
|
||||
|
||||
### Alerts
|
||||
|
||||
Status and notification messages:
|
||||
|
||||
```html
|
||||
<!-- Success Alert -->
|
||||
<div class="alert alert--success" role="alert">
|
||||
<div class="alert__icon">✅</div>
|
||||
<div class="alert__content">
|
||||
<div class="alert__title">Success</div>
|
||||
<div class="alert__message">Operation completed successfully!</div>
|
||||
</div>
|
||||
<button class="alert__close" aria-label="Close alert">×</button>
|
||||
</div>
|
||||
|
||||
<!-- Error Alert -->
|
||||
<div class="alert alert--error" role="alert">
|
||||
<div class="alert__icon">❌</div>
|
||||
<div class="alert__content">
|
||||
<div class="alert__title">Error</div>
|
||||
<div class="alert__message">Something went wrong. Please try again.</div>
|
||||
</div>
|
||||
<button class="alert__close" aria-label="Close alert">×</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Loading States
|
||||
|
||||
Progress indicators and skeleton screens:
|
||||
|
||||
```html
|
||||
<!-- Spinner -->
|
||||
<div class="spinner" role="status" aria-label="Loading">
|
||||
<div class="spinner__circle"></div>
|
||||
</div>
|
||||
|
||||
<!-- Progress Bar -->
|
||||
<div class="progress" role="progressbar" aria-valuenow="65" aria-valuemin="0" aria-valuemax="100">
|
||||
<div class="progress__bar" style="width: 65%"></div>
|
||||
<div class="progress__label">65% Complete</div>
|
||||
</div>
|
||||
|
||||
<!-- Skeleton Screen -->
|
||||
<div class="skeleton">
|
||||
<div class="skeleton__line skeleton__line--title"></div>
|
||||
<div class="skeleton__line skeleton__line--text"></div>
|
||||
<div class="skeleton__line skeleton__line--text skeleton__line--short"></div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## 🎯 Interactive Components
|
||||
|
||||
### Tabs
|
||||
|
||||
Content organization with tabbed interface:
|
||||
|
||||
```html
|
||||
<div class="tabs" role="tablist">
|
||||
<button class="tabs__tab tabs__tab--active" role="tab" aria-selected="true" aria-controls="panel-1">
|
||||
Tab 1
|
||||
</button>
|
||||
<button class="tabs__tab" role="tab" aria-selected="false" aria-controls="panel-2">
|
||||
Tab 2
|
||||
</button>
|
||||
<button class="tabs__tab" role="tab" aria-selected="false" aria-controls="panel-3">
|
||||
Tab 3
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="tabs__panels">
|
||||
<div class="tabs__panel tabs__panel--active" role="tabpanel" id="panel-1">
|
||||
Content for tab 1
|
||||
</div>
|
||||
<div class="tabs__panel" role="tabpanel" id="panel-2" hidden>
|
||||
Content for tab 2
|
||||
</div>
|
||||
<div class="tabs__panel" role="tabpanel" id="panel-3" hidden>
|
||||
Content for tab 3
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Dropdowns
|
||||
|
||||
Menu and selection dropdowns:
|
||||
|
||||
```html
|
||||
<div class="dropdown">
|
||||
<button class="dropdown__trigger" aria-haspopup="true" aria-expanded="false">
|
||||
Menu
|
||||
<span class="dropdown__arrow">▼</span>
|
||||
</button>
|
||||
<ul class="dropdown__menu" role="menu">
|
||||
<li role="none">
|
||||
<a class="dropdown__item" role="menuitem" href="#action1">Action 1</a>
|
||||
</li>
|
||||
<li role="none">
|
||||
<a class="dropdown__item" role="menuitem" href="#action2">Action 2</a>
|
||||
</li>
|
||||
<li class="dropdown__divider" role="separator"></li>
|
||||
<li role="none">
|
||||
<a class="dropdown__item dropdown__item--danger" role="menuitem" href="#delete">
|
||||
Delete Item
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
```
|
||||
|
||||
## ♿ Accessibility Guidelines
|
||||
|
||||
### ARIA Implementation
|
||||
|
||||
All components include proper ARIA attributes:
|
||||
|
||||
- `role` attributes for semantic meaning
|
||||
- `aria-label` for accessible names
|
||||
- `aria-describedby` for help text associations
|
||||
- `aria-expanded` for collapsible content
|
||||
- `aria-selected` for selectable items
|
||||
|
||||
### Keyboard Navigation
|
||||
|
||||
Components support keyboard interaction:
|
||||
|
||||
- **Tab**: Navigate between focusable elements
|
||||
- **Enter/Space**: Activate buttons and links
|
||||
- **Escape**: Close modals and dropdowns
|
||||
- **Arrow Keys**: Navigate within component groups
|
||||
|
||||
### Focus Management
|
||||
|
||||
Proper focus indicators and management:
|
||||
|
||||
```css
|
||||
.component:focus-visible {
|
||||
outline: 2px solid var(--color-primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Skip focus ring on mouse interaction */
|
||||
.component:focus:not(:focus-visible) {
|
||||
outline: none;
|
||||
}
|
||||
```
|
||||
|
||||
## 🎨 Theming
|
||||
|
||||
All components automatically adapt to theme changes through CSS custom properties:
|
||||
|
||||
```css
|
||||
.component {
|
||||
background: var(--surface);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
/* Dark theme automatically applied */
|
||||
[data-theme="dark"] .component {
|
||||
/* Inherits dark theme tokens */
|
||||
}
|
||||
```
|
||||
|
||||
## 📚 Usage Examples
|
||||
|
||||
### JavaScript Integration
|
||||
|
||||
Components work with the framework's JavaScript module system:
|
||||
|
||||
```javascript
|
||||
// Auto-initialize components
|
||||
document.querySelectorAll('[data-component="modal"]').forEach(element => {
|
||||
new Modal(element);
|
||||
});
|
||||
|
||||
// Event-driven interactions
|
||||
events.delegate('click', '[data-action="show-alert"]', (event, element) => {
|
||||
const message = element.dataset.message;
|
||||
showAlert('success', message);
|
||||
});
|
||||
```
|
||||
|
||||
### PHP Template Integration
|
||||
|
||||
Components integrate with the View system:
|
||||
|
||||
```php
|
||||
<div class="card">
|
||||
<div class="card__header">
|
||||
<h3 class="card__title"><?= $title ?></h3>
|
||||
</div>
|
||||
<div class="card__body">
|
||||
<?= $content ?>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*For design tokens and foundations, see [Design Foundations](foundations.md)*
|
||||
*For CSS architecture details, see [CSS Architecture](css-architecture.md)*
|
||||
*For JavaScript integration, see [JavaScript Modules](javascript.md)*
|
||||
@@ -0,0 +1,252 @@
|
||||
# CSS Architecture
|
||||
|
||||
> ITCSS-based modular CSS architecture with component-driven development and utility-first approach.
|
||||
|
||||
## 📁 File Structure
|
||||
|
||||
```
|
||||
resources/css/
|
||||
├── styles.css # Main import file
|
||||
├── settings/
|
||||
│ ├── colors.css # Color definitions
|
||||
│ ├── typography.css # Font sizes, fonts
|
||||
│ ├── spacing.css # Spacing (margin, padding)
|
||||
│ └── variables.css # Duration, easing, radius, z-index etc.
|
||||
├── base/
|
||||
│ ├── reset.css # Reset/Normalize
|
||||
│ ├── global.css # Global styles for html, body, etc.
|
||||
│ ├── typography.css # h1, p, etc.
|
||||
│ ├── focus.css # Focus states
|
||||
│ ├── media.css # Media queries
|
||||
│ └── index.css # Base layer imports
|
||||
├── layout/
|
||||
│ ├── container.css # .page-container, max-widths, etc.
|
||||
│ └── grid.css # Custom grid system
|
||||
├── components/
|
||||
│ ├── header.css
|
||||
│ ├── nav.css
|
||||
│ ├── footer.css
|
||||
│ ├── buttons.css
|
||||
│ ├── card.css
|
||||
│ └── sidebar.css
|
||||
├── forms/
|
||||
│ └── inputs.css
|
||||
├── utilities/
|
||||
│ ├── animations.css # .fade-in, .shake, etc.
|
||||
│ ├── helpers.css # .skip-link, .hidden, .visually-hidden
|
||||
│ ├── scroll.css # scroll-behavior, scrollbar-style
|
||||
│ ├── transitions.css
|
||||
│ └── noise.css
|
||||
└── themes/
|
||||
└── dark.css # Dark mode color adjustments
|
||||
```
|
||||
|
||||
## 🏗 ITCSS Layer Hierarchy
|
||||
|
||||
1. **Settings** - Variables, colors, typography scales
|
||||
2. **Base** - Reset, normalize, global element styles
|
||||
3. **Layout** - Grid systems, containers, structural components
|
||||
4. **Components** - UI components and modules
|
||||
5. **Utilities** - Helper classes and overrides
|
||||
6. **Themes** - Color scheme variations
|
||||
|
||||
## 🎨 Design Tokens
|
||||
|
||||
### Colors
|
||||
```css
|
||||
/* Primary Palette */
|
||||
--color-primary: #007bff;
|
||||
--color-primary-dark: #0056b3;
|
||||
--color-primary-light: #66b3ff;
|
||||
|
||||
/* Semantic Colors */
|
||||
--color-success: #28a745;
|
||||
--color-warning: #ffc107;
|
||||
--color-error: #dc3545;
|
||||
--color-info: #17a2b8;
|
||||
|
||||
/* Neutral Palette */
|
||||
--color-gray-50: #f8f9fa;
|
||||
--color-gray-100: #e9ecef;
|
||||
--color-gray-900: #212529;
|
||||
```
|
||||
|
||||
### Typography
|
||||
```css
|
||||
/* Font Stacks */
|
||||
--font-family-sans: system-ui, -apple-system, sans-serif;
|
||||
--font-family-mono: 'SF Mono', Monaco, 'Cascadia Code', monospace;
|
||||
|
||||
/* Type Scale */
|
||||
--font-size-xs: 0.75rem; /* 12px */
|
||||
--font-size-sm: 0.875rem; /* 14px */
|
||||
--font-size-base: 1rem; /* 16px */
|
||||
--font-size-lg: 1.125rem; /* 18px */
|
||||
--font-size-xl: 1.25rem; /* 20px */
|
||||
--font-size-2xl: 1.5rem; /* 24px */
|
||||
--font-size-3xl: 1.875rem; /* 30px */
|
||||
--font-size-4xl: 2.25rem; /* 36px */
|
||||
```
|
||||
|
||||
### Spacing System
|
||||
```css
|
||||
/* 8pt Grid System */
|
||||
--space-1: 0.25rem; /* 4px */
|
||||
--space-2: 0.5rem; /* 8px */
|
||||
--space-3: 0.75rem; /* 12px */
|
||||
--space-4: 1rem; /* 16px */
|
||||
--space-5: 1.25rem; /* 20px */
|
||||
--space-6: 1.5rem; /* 24px */
|
||||
--space-8: 2rem; /* 32px */
|
||||
--space-10: 2.5rem; /* 40px */
|
||||
--space-12: 3rem; /* 48px */
|
||||
--space-16: 4rem; /* 64px */
|
||||
--space-20: 5rem; /* 80px */
|
||||
```
|
||||
|
||||
## 🧩 Component Patterns
|
||||
|
||||
### BEM Methodology
|
||||
```css
|
||||
/* Block */
|
||||
.card { }
|
||||
|
||||
/* Element */
|
||||
.card__header { }
|
||||
.card__body { }
|
||||
.card__footer { }
|
||||
|
||||
/* Modifier */
|
||||
.card--large { }
|
||||
.card--featured { }
|
||||
.card__header--centered { }
|
||||
```
|
||||
|
||||
### Component States
|
||||
```css
|
||||
.button {
|
||||
/* Base styles */
|
||||
|
||||
&:hover { }
|
||||
&:focus { }
|
||||
&:active { }
|
||||
&:disabled { }
|
||||
|
||||
&[aria-pressed="true"] { }
|
||||
&[aria-expanded="true"] { }
|
||||
}
|
||||
```
|
||||
|
||||
## 📱 Responsive Design
|
||||
|
||||
### Breakpoints
|
||||
```css
|
||||
/* Mobile First Approach */
|
||||
--breakpoint-sm: 640px;
|
||||
--breakpoint-md: 768px;
|
||||
--breakpoint-lg: 1024px;
|
||||
--breakpoint-xl: 1280px;
|
||||
--breakpoint-2xl: 1536px;
|
||||
```
|
||||
|
||||
### Media Query Mixins
|
||||
```css
|
||||
@media (min-width: 768px) {
|
||||
/* Tablet and up */
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
/* Desktop and up */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
/* Reduced motion accessibility */
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
/* Dark mode preferences */
|
||||
}
|
||||
```
|
||||
|
||||
## 🎯 Admin-Specific Extensions
|
||||
|
||||
### Admin Color Palette
|
||||
```css
|
||||
/* Admin Theme Colors */
|
||||
--admin-primary: #6366f1;
|
||||
--admin-secondary: #8b5cf6;
|
||||
--admin-success: #10b981;
|
||||
--admin-warning: #f59e0b;
|
||||
--admin-error: #ef4444;
|
||||
|
||||
/* Admin Neutral Palette */
|
||||
--admin-bg: #f8fafc;
|
||||
--admin-surface: #ffffff;
|
||||
--admin-border: #e2e8f0;
|
||||
--admin-text: #1e293b;
|
||||
--admin-text-muted: #64748b;
|
||||
```
|
||||
|
||||
### Admin Components
|
||||
```css
|
||||
/* Admin Layout */
|
||||
.admin-layout { }
|
||||
.admin-header { }
|
||||
.admin-nav { }
|
||||
.admin-main { }
|
||||
.admin-sidebar { }
|
||||
|
||||
/* Admin UI Components */
|
||||
.admin-card { }
|
||||
.admin-table { }
|
||||
.admin-button { }
|
||||
.admin-form { }
|
||||
.admin-stats { }
|
||||
```
|
||||
|
||||
## 🔧 Build Process
|
||||
|
||||
### CSS Processing
|
||||
1. **PostCSS** for modern CSS features
|
||||
2. **Autoprefixer** for browser compatibility
|
||||
3. **CSS Nano** for minification
|
||||
4. **PurgeCSS** for unused style removal
|
||||
|
||||
### Development Workflow
|
||||
```bash
|
||||
# Development with hot reload
|
||||
npm run dev
|
||||
|
||||
# Production build
|
||||
npm run build
|
||||
|
||||
# Watch for changes
|
||||
npm run watch
|
||||
```
|
||||
|
||||
## 📋 Best Practices
|
||||
|
||||
### Performance
|
||||
- Use CSS custom properties for theme values
|
||||
- Minimize selector specificity
|
||||
- Leverage cascade and inheritance
|
||||
- Use `contain` property for layout optimization
|
||||
- Implement critical CSS loading
|
||||
|
||||
### Maintainability
|
||||
- Follow BEM naming conventions
|
||||
- Keep components isolated and reusable
|
||||
- Use meaningful class names
|
||||
- Document complex selectors
|
||||
- Regular architecture reviews
|
||||
|
||||
### Accessibility
|
||||
- Ensure sufficient color contrast ratios
|
||||
- Support reduced motion preferences
|
||||
- Use semantic markup structure
|
||||
- Implement focus management
|
||||
- Test with assistive technologies
|
||||
|
||||
---
|
||||
|
||||
*For component examples and live demos, visit [Admin Style Guide](/admin/docs)*
|
||||
365
backups/docs-backup-20250731125004/design-system/foundations.md
Normal file
365
backups/docs-backup-20250731125004/design-system/foundations.md
Normal file
@@ -0,0 +1,365 @@
|
||||
# Design System Foundations
|
||||
|
||||
> Core design principles, tokens, and guidelines for consistent visual language.
|
||||
|
||||
## 🎨 Color System
|
||||
|
||||
### Primary Palette
|
||||
```css
|
||||
--color-primary: #6366f1; /* Indigo - Main brand color */
|
||||
--color-primary-50: #eef2ff; /* Lightest tint */
|
||||
--color-primary-100: #e0e7ff;
|
||||
--color-primary-200: #c7d2fe;
|
||||
--color-primary-300: #a5b4fc;
|
||||
--color-primary-400: #818cf8;
|
||||
--color-primary-500: #6366f1; /* Base */
|
||||
--color-primary-600: #4f46e5;
|
||||
--color-primary-700: #4338ca;
|
||||
--color-primary-800: #3730a3;
|
||||
--color-primary-900: #312e81; /* Darkest shade */
|
||||
```
|
||||
|
||||
### Semantic Colors
|
||||
```css
|
||||
/* Success - Green */
|
||||
--color-success: #10b981;
|
||||
--color-success-light: #d1fae5;
|
||||
--color-success-dark: #047857;
|
||||
|
||||
/* Warning - Amber */
|
||||
--color-warning: #f59e0b;
|
||||
--color-warning-light: #fef3c7;
|
||||
--color-warning-dark: #d97706;
|
||||
|
||||
/* Error - Red */
|
||||
--color-error: #ef4444;
|
||||
--color-error-light: #fee2e2;
|
||||
--color-error-dark: #dc2626;
|
||||
|
||||
/* Info - Blue */
|
||||
--color-info: #3b82f6;
|
||||
--color-info-light: #dbeafe;
|
||||
--color-info-dark: #1d4ed8;
|
||||
```
|
||||
|
||||
### Neutral Palette
|
||||
```css
|
||||
--color-gray-50: #f8fafc;
|
||||
--color-gray-100: #f1f5f9;
|
||||
--color-gray-200: #e2e8f0;
|
||||
--color-gray-300: #cbd5e1;
|
||||
--color-gray-400: #94a3b8;
|
||||
--color-gray-500: #64748b;
|
||||
--color-gray-600: #475569;
|
||||
--color-gray-700: #334155;
|
||||
--color-gray-800: #1e293b;
|
||||
--color-gray-900: #0f172a;
|
||||
```
|
||||
|
||||
## 📝 Typography
|
||||
|
||||
### Font Stacks
|
||||
```css
|
||||
/* Sans Serif - Primary */
|
||||
--font-family-sans: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
|
||||
|
||||
/* Monospace - Code */
|
||||
--font-family-mono: 'SF Mono', 'Monaco', 'Cascadia Code', 'Fira Code', monospace;
|
||||
|
||||
/* Serif - Optional */
|
||||
--font-family-serif: 'Charter', 'Georgia', 'Times New Roman', serif;
|
||||
```
|
||||
|
||||
### Type Scale
|
||||
```css
|
||||
/* Font Sizes (16px base) */
|
||||
--text-xs: 0.75rem; /* 12px */
|
||||
--text-sm: 0.875rem; /* 14px */
|
||||
--text-base: 1rem; /* 16px */
|
||||
--text-lg: 1.125rem; /* 18px */
|
||||
--text-xl: 1.25rem; /* 20px */
|
||||
--text-2xl: 1.5rem; /* 24px */
|
||||
--text-3xl: 1.875rem; /* 30px */
|
||||
--text-4xl: 2.25rem; /* 36px */
|
||||
--text-5xl: 3rem; /* 48px */
|
||||
--text-6xl: 3.75rem; /* 60px */
|
||||
--text-7xl: 4.5rem; /* 72px */
|
||||
--text-8xl: 6rem; /* 96px */
|
||||
--text-9xl: 8rem; /* 128px */
|
||||
```
|
||||
|
||||
### Font Weights
|
||||
```css
|
||||
--font-thin: 100;
|
||||
--font-extralight: 200;
|
||||
--font-light: 300;
|
||||
--font-normal: 400;
|
||||
--font-medium: 500;
|
||||
--font-semibold: 600;
|
||||
--font-bold: 700;
|
||||
--font-extrabold: 800;
|
||||
--font-black: 900;
|
||||
```
|
||||
|
||||
### Line Heights
|
||||
```css
|
||||
--leading-none: 1;
|
||||
--leading-tight: 1.25;
|
||||
--leading-snug: 1.375;
|
||||
--leading-normal: 1.5;
|
||||
--leading-relaxed: 1.625;
|
||||
--leading-loose: 2;
|
||||
```
|
||||
|
||||
## 📏 Spacing System
|
||||
|
||||
### Base Unit: 4px
|
||||
```css
|
||||
--space-0: 0;
|
||||
--space-px: 1px;
|
||||
--space-0-5: 0.125rem; /* 2px */
|
||||
--space-1: 0.25rem; /* 4px */
|
||||
--space-1-5: 0.375rem; /* 6px */
|
||||
--space-2: 0.5rem; /* 8px */
|
||||
--space-2-5: 0.625rem; /* 10px */
|
||||
--space-3: 0.75rem; /* 12px */
|
||||
--space-3-5: 0.875rem; /* 14px */
|
||||
--space-4: 1rem; /* 16px */
|
||||
--space-5: 1.25rem; /* 20px */
|
||||
--space-6: 1.5rem; /* 24px */
|
||||
--space-7: 1.75rem; /* 28px */
|
||||
--space-8: 2rem; /* 32px */
|
||||
--space-9: 2.25rem; /* 36px */
|
||||
--space-10: 2.5rem; /* 40px */
|
||||
--space-11: 2.75rem; /* 44px */
|
||||
--space-12: 3rem; /* 48px */
|
||||
--space-14: 3.5rem; /* 56px */
|
||||
--space-16: 4rem; /* 64px */
|
||||
--space-20: 5rem; /* 80px */
|
||||
--space-24: 6rem; /* 96px */
|
||||
--space-28: 7rem; /* 112px */
|
||||
--space-32: 8rem; /* 128px */
|
||||
--space-36: 9rem; /* 144px */
|
||||
--space-40: 10rem; /* 160px */
|
||||
--space-44: 11rem; /* 176px */
|
||||
--space-48: 12rem; /* 192px */
|
||||
--space-52: 13rem; /* 208px */
|
||||
--space-56: 14rem; /* 224px */
|
||||
--space-60: 15rem; /* 240px */
|
||||
--space-64: 16rem; /* 256px */
|
||||
--space-72: 18rem; /* 288px */
|
||||
--space-80: 20rem; /* 320px */
|
||||
--space-96: 24rem; /* 384px */
|
||||
```
|
||||
|
||||
## 🔲 Border Radius
|
||||
|
||||
```css
|
||||
--radius-none: 0;
|
||||
--radius-sm: 0.125rem; /* 2px */
|
||||
--radius: 0.25rem; /* 4px */
|
||||
--radius-md: 0.375rem; /* 6px */
|
||||
--radius-lg: 0.5rem; /* 8px */
|
||||
--radius-xl: 0.75rem; /* 12px */
|
||||
--radius-2xl: 1rem; /* 16px */
|
||||
--radius-3xl: 1.5rem; /* 24px */
|
||||
--radius-full: 9999px; /* Pill shape */
|
||||
```
|
||||
|
||||
## 🌫 Shadows & Elevation
|
||||
|
||||
```css
|
||||
/* Box Shadows */
|
||||
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||
--shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
|
||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||
--shadow-2xl: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
||||
--shadow-inner: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
|
||||
|
||||
/* Colored Shadows */
|
||||
--shadow-primary: 0 4px 14px 0 rgba(99, 102, 241, 0.15);
|
||||
--shadow-success: 0 4px 14px 0 rgba(16, 185, 129, 0.15);
|
||||
--shadow-warning: 0 4px 14px 0 rgba(245, 158, 11, 0.15);
|
||||
--shadow-error: 0 4px 14px 0 rgba(239, 68, 68, 0.15);
|
||||
```
|
||||
|
||||
## 📐 Layout & Grid
|
||||
|
||||
### Breakpoints
|
||||
```css
|
||||
--breakpoint-sm: 640px; /* Small tablets */
|
||||
--breakpoint-md: 768px; /* Large tablets */
|
||||
--breakpoint-lg: 1024px; /* Laptops */
|
||||
--breakpoint-xl: 1280px; /* Desktops */
|
||||
--breakpoint-2xl: 1536px; /* Large desktops */
|
||||
```
|
||||
|
||||
### Container Sizes
|
||||
```css
|
||||
--container-sm: 640px;
|
||||
--container-md: 768px;
|
||||
--container-lg: 1024px;
|
||||
--container-xl: 1280px;
|
||||
--container-2xl: 1536px;
|
||||
```
|
||||
|
||||
### Z-Index Scale
|
||||
```css
|
||||
--z-auto: auto;
|
||||
--z-0: 0;
|
||||
--z-10: 10;
|
||||
--z-20: 20;
|
||||
--z-30: 30;
|
||||
--z-40: 40;
|
||||
--z-50: 50;
|
||||
|
||||
/* Named layers */
|
||||
--z-dropdown: 1000;
|
||||
--z-sticky: 1020;
|
||||
--z-fixed: 1030;
|
||||
--z-modal-backdrop: 1040;
|
||||
--z-modal: 1050;
|
||||
--z-popover: 1060;
|
||||
--z-tooltip: 1070;
|
||||
--z-toast: 1080;
|
||||
```
|
||||
|
||||
## ⏱ Motion & Animation
|
||||
|
||||
### Duration
|
||||
```css
|
||||
--duration-75: 75ms;
|
||||
--duration-100: 100ms;
|
||||
--duration-150: 150ms;
|
||||
--duration-200: 200ms;
|
||||
--duration-300: 300ms;
|
||||
--duration-500: 500ms;
|
||||
--duration-700: 700ms;
|
||||
--duration-1000: 1000ms;
|
||||
```
|
||||
|
||||
### Easing Functions
|
||||
```css
|
||||
--ease-linear: linear;
|
||||
--ease-in: cubic-bezier(0.4, 0, 1, 1);
|
||||
--ease-out: cubic-bezier(0, 0, 0.2, 1);
|
||||
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
/* Custom easing */
|
||||
--ease-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||
--ease-smooth: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
```
|
||||
|
||||
## 🎭 Theme System
|
||||
|
||||
### Light Theme (Default)
|
||||
```css
|
||||
:root {
|
||||
--background: var(--color-gray-50);
|
||||
--surface: #ffffff;
|
||||
--surface-variant: var(--color-gray-100);
|
||||
--border: var(--color-gray-200);
|
||||
--text-primary: var(--color-gray-900);
|
||||
--text-secondary: var(--color-gray-600);
|
||||
--text-muted: var(--color-gray-500);
|
||||
--text-disabled: var(--color-gray-400);
|
||||
}
|
||||
```
|
||||
|
||||
### Dark Theme
|
||||
```css
|
||||
[data-theme="dark"] {
|
||||
--background: var(--color-gray-900);
|
||||
--surface: var(--color-gray-800);
|
||||
--surface-variant: var(--color-gray-700);
|
||||
--border: var(--color-gray-600);
|
||||
--text-primary: var(--color-gray-50);
|
||||
--text-secondary: var(--color-gray-300);
|
||||
--text-muted: var(--color-gray-400);
|
||||
--text-disabled: var(--color-gray-500);
|
||||
|
||||
/* Adjust shadows for dark mode */
|
||||
--shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.3), 0 1px 2px 0 rgba(0, 0, 0, 0.2);
|
||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -1px rgba(0, 0, 0, 0.2);
|
||||
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -2px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
```
|
||||
|
||||
## 📊 Usage Guidelines
|
||||
|
||||
### Semantic Token Usage
|
||||
|
||||
```css
|
||||
/* Use semantic tokens for meaning */
|
||||
.success-message {
|
||||
color: var(--color-success);
|
||||
background: var(--color-success-light);
|
||||
border-color: var(--color-success);
|
||||
}
|
||||
|
||||
/* Use scale tokens for consistency */
|
||||
.card {
|
||||
padding: var(--space-6);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
/* Use theme tokens for adaptability */
|
||||
.content {
|
||||
background: var(--surface);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
```
|
||||
|
||||
### Responsive Design
|
||||
|
||||
```css
|
||||
/* Mobile first approach */
|
||||
.component {
|
||||
padding: var(--space-4);
|
||||
font-size: var(--text-sm);
|
||||
|
||||
@media (min-width: 768px) {
|
||||
padding: var(--space-6);
|
||||
font-size: var(--text-base);
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
padding: var(--space-8);
|
||||
font-size: var(--text-lg);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Accessibility Considerations
|
||||
|
||||
```css
|
||||
/* Ensure sufficient contrast */
|
||||
.text-on-primary {
|
||||
color: white; /* Passes WCAG AA on primary blue */
|
||||
}
|
||||
|
||||
/* Respect motion preferences */
|
||||
.animated-element {
|
||||
transition: transform var(--duration-300) var(--ease-out);
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Focus indicators */
|
||||
.interactive-element:focus-visible {
|
||||
outline: 2px solid var(--color-primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*For implementation examples, see [CSS Architecture](css-architecture.md)*
|
||||
*For component usage, see [UI Components](components.md)*
|
||||
*For admin-specific patterns, see [Admin Interface](admin/overview.md)*
|
||||
642
backups/docs-backup-20250731125004/design-system/javascript.md
Normal file
642
backups/docs-backup-20250731125004/design-system/javascript.md
Normal file
@@ -0,0 +1,642 @@
|
||||
# JavaScript Module System
|
||||
|
||||
> Modern ES6+ modular JavaScript architecture with performance monitoring, state management, and component-based UI interactions.
|
||||
|
||||
## 📁 Module Structure
|
||||
|
||||
```
|
||||
resources/js/
|
||||
├── main.js # Entry point
|
||||
├── core/ # Core framework modules
|
||||
│ ├── index.js # Core exports
|
||||
│ ├── init.js # Initialization
|
||||
│ ├── router.js # SPA routing
|
||||
│ ├── state.js # State management
|
||||
│ ├── EventManager.js # Event system
|
||||
│ ├── PerformanceMonitor.js # Performance tracking
|
||||
│ └── logger.js # Logging utilities
|
||||
├── modules/ # Feature modules
|
||||
│ ├── index.js # Module registry
|
||||
│ ├── ui/ # UI components
|
||||
│ │ ├── UIManager.js # UI coordinator
|
||||
│ │ └── components/ # Individual components
|
||||
│ ├── scroll-fx/ # Scroll animations
|
||||
│ ├── lightbox/ # Image lightbox
|
||||
│ └── parallax/ # Parallax effects
|
||||
├── utils/ # Utility functions
|
||||
└── docs/ # Module documentation
|
||||
```
|
||||
|
||||
## 🚀 Core System
|
||||
|
||||
### Application Initialization
|
||||
|
||||
```javascript
|
||||
// main.js - Application entry point
|
||||
import { init } from './core/init.js';
|
||||
import { ModuleRegistry } from './modules/index.js';
|
||||
|
||||
// Initialize core systems
|
||||
await init({
|
||||
performance: true,
|
||||
router: true,
|
||||
state: true,
|
||||
logging: 'development'
|
||||
});
|
||||
|
||||
// Register and load modules
|
||||
ModuleRegistry.register('ui', () => import('./modules/ui/index.js'));
|
||||
ModuleRegistry.register('scrollfx', () => import('./modules/scrollfx/index.js'));
|
||||
|
||||
// Auto-load modules based on DOM attributes
|
||||
ModuleRegistry.autoLoad();
|
||||
```
|
||||
|
||||
### Router System
|
||||
|
||||
```javascript
|
||||
// SPA routing with layout animations
|
||||
import { Router } from './core/router.js';
|
||||
|
||||
const router = new Router({
|
||||
mode: 'history',
|
||||
base: '/',
|
||||
transitions: true,
|
||||
prefetch: true
|
||||
});
|
||||
|
||||
// Route definitions
|
||||
router.addRoute('/admin/:page?', async (ctx) => {
|
||||
const { page = 'dashboard' } = ctx.params;
|
||||
|
||||
// Layout animation
|
||||
if (ctx.isLayoutChange) {
|
||||
await animateLayoutSwitch('admin');
|
||||
}
|
||||
|
||||
// Load page content
|
||||
return await loadAdminPage(page);
|
||||
});
|
||||
|
||||
// Meta data extraction from HTML
|
||||
router.onNavigate((ctx) => {
|
||||
// Extract and apply meta data
|
||||
const metaTitle = ctx.dom.querySelector('[data-meta-title]');
|
||||
if (metaTitle) {
|
||||
document.title = metaTitle.dataset.metaTitle;
|
||||
}
|
||||
|
||||
const metaTheme = ctx.dom.querySelector('[data-meta-theme]');
|
||||
if (metaTheme) {
|
||||
document.documentElement.style.setProperty('--theme-color', metaTheme.dataset.metaTheme);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### State Management
|
||||
|
||||
```javascript
|
||||
// Reactive state system
|
||||
import { State } from './core/state.js';
|
||||
|
||||
// Global state
|
||||
const appState = new State({
|
||||
user: null,
|
||||
theme: 'light',
|
||||
admin: {
|
||||
currentPage: 'dashboard',
|
||||
notifications: []
|
||||
}
|
||||
});
|
||||
|
||||
// Reactive updates
|
||||
appState.watch('theme', (newTheme, oldTheme) => {
|
||||
document.documentElement.dataset.theme = newTheme;
|
||||
localStorage.setItem('preferred-theme', newTheme);
|
||||
});
|
||||
|
||||
// Component state binding
|
||||
appState.bind('[data-user-name]', 'user.name');
|
||||
appState.bind('[data-notification-count]', 'admin.notifications.length');
|
||||
```
|
||||
|
||||
### Event System
|
||||
|
||||
```javascript
|
||||
// Centralized event management
|
||||
import { EventManager } from './core/EventManager.js';
|
||||
|
||||
const events = new EventManager();
|
||||
|
||||
// Global event delegation
|
||||
events.delegate('click', '[data-action]', (event, element) => {
|
||||
const action = element.dataset.action;
|
||||
const target = element.dataset.target;
|
||||
|
||||
switch (action) {
|
||||
case 'toggle-theme':
|
||||
appState.set('theme', appState.get('theme') === 'light' ? 'dark' : 'light');
|
||||
break;
|
||||
case 'show-modal':
|
||||
UIManager.showModal(target);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// Custom events
|
||||
events.on('admin:page-change', (data) => {
|
||||
appState.set('admin.currentPage', data.page);
|
||||
PerformanceMonitor.mark(`admin-${data.page}-loaded`);
|
||||
});
|
||||
```
|
||||
|
||||
## 🧩 UI Component System
|
||||
|
||||
### Component Architecture
|
||||
|
||||
```javascript
|
||||
// Base component class
|
||||
class BaseComponent {
|
||||
constructor(element, options = {}) {
|
||||
this.element = element;
|
||||
this.options = { ...this.defaults, ...options };
|
||||
this.state = new State(this.initialState);
|
||||
|
||||
this.init();
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
init() {
|
||||
// Override in subclasses
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
// Override in subclasses
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.state.destroy();
|
||||
this.element.removeEventListener();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Modal Component
|
||||
|
||||
```javascript
|
||||
// UI Modal component
|
||||
class Modal extends BaseComponent {
|
||||
defaults = {
|
||||
closeOnOverlay: true,
|
||||
closeOnEscape: true,
|
||||
animation: 'fade'
|
||||
};
|
||||
|
||||
initialState = {
|
||||
isOpen: false,
|
||||
content: null
|
||||
};
|
||||
|
||||
init() {
|
||||
this.overlay = this.element.querySelector('.modal-overlay');
|
||||
this.content = this.element.querySelector('.modal-content');
|
||||
this.closeBtn = this.element.querySelector('[data-modal-close]');
|
||||
|
||||
// State reactivity
|
||||
this.state.watch('isOpen', (isOpen) => {
|
||||
this.element.classList.toggle('is-open', isOpen);
|
||||
this.element.setAttribute('aria-hidden', !isOpen);
|
||||
|
||||
if (isOpen) {
|
||||
this.trapFocus();
|
||||
} else {
|
||||
this.releaseFocus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
if (this.closeBtn) {
|
||||
this.closeBtn.addEventListener('click', () => this.close());
|
||||
}
|
||||
|
||||
if (this.options.closeOnOverlay) {
|
||||
this.overlay.addEventListener('click', () => this.close());
|
||||
}
|
||||
|
||||
if (this.options.closeOnEscape) {
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && this.state.get('isOpen')) {
|
||||
this.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
open(content = null) {
|
||||
if (content) {
|
||||
this.setContent(content);
|
||||
}
|
||||
this.state.set('isOpen', true);
|
||||
events.emit('modal:opened', { modal: this });
|
||||
}
|
||||
|
||||
close() {
|
||||
this.state.set('isOpen', false);
|
||||
events.emit('modal:closed', { modal: this });
|
||||
}
|
||||
|
||||
setContent(content) {
|
||||
if (typeof content === 'string') {
|
||||
this.content.innerHTML = content;
|
||||
} else if (content instanceof HTMLElement) {
|
||||
this.content.innerHTML = '';
|
||||
this.content.appendChild(content);
|
||||
}
|
||||
this.state.set('content', content);
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-initialization
|
||||
document.querySelectorAll('[data-modal]').forEach(element => {
|
||||
new Modal(element);
|
||||
});
|
||||
```
|
||||
|
||||
### Admin-specific Components
|
||||
|
||||
```javascript
|
||||
// Admin Stats Card
|
||||
class AdminStatsCard extends BaseComponent {
|
||||
defaults = {
|
||||
updateInterval: 30000,
|
||||
animateChanges: true
|
||||
};
|
||||
|
||||
init() {
|
||||
this.valueElement = this.element.querySelector('.stat-value');
|
||||
this.labelElement = this.element.querySelector('.stat-label');
|
||||
this.trendElement = this.element.querySelector('.stat-trend');
|
||||
|
||||
if (this.options.updateInterval) {
|
||||
this.startPolling();
|
||||
}
|
||||
}
|
||||
|
||||
startPolling() {
|
||||
this.pollInterval = setInterval(() => {
|
||||
this.updateValue();
|
||||
}, this.options.updateInterval);
|
||||
}
|
||||
|
||||
async updateValue() {
|
||||
const endpoint = this.element.dataset.endpoint;
|
||||
if (!endpoint) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(endpoint);
|
||||
const data = await response.json();
|
||||
|
||||
if (this.options.animateChanges) {
|
||||
this.animateValueChange(data.value);
|
||||
} else {
|
||||
this.setValue(data.value);
|
||||
}
|
||||
|
||||
if (data.trend) {
|
||||
this.setTrend(data.trend);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to update stat:', error);
|
||||
}
|
||||
}
|
||||
|
||||
animateValueChange(newValue) {
|
||||
const currentValue = parseInt(this.valueElement.textContent) || 0;
|
||||
const duration = 1000;
|
||||
const steps = 60;
|
||||
const increment = (newValue - currentValue) / steps;
|
||||
|
||||
let step = 0;
|
||||
const timer = setInterval(() => {
|
||||
step++;
|
||||
const value = Math.round(currentValue + (increment * step));
|
||||
this.valueElement.textContent = value.toLocaleString();
|
||||
|
||||
if (step >= steps) {
|
||||
clearInterval(timer);
|
||||
this.valueElement.textContent = newValue.toLocaleString();
|
||||
}
|
||||
}, duration / steps);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 📊 Performance Monitoring
|
||||
|
||||
```javascript
|
||||
// Performance tracking
|
||||
import { PerformanceMonitor } from './core/PerformanceMonitor.js';
|
||||
|
||||
// Page load metrics
|
||||
PerformanceMonitor.mark('page-start');
|
||||
PerformanceMonitor.measure('page-load', 'page-start', 'page-end');
|
||||
|
||||
// Component performance
|
||||
class ComponentWithMetrics extends BaseComponent {
|
||||
init() {
|
||||
PerformanceMonitor.mark(`${this.constructor.name}-init-start`);
|
||||
|
||||
// Component initialization
|
||||
super.init();
|
||||
|
||||
PerformanceMonitor.mark(`${this.constructor.name}-init-end`);
|
||||
PerformanceMonitor.measure(
|
||||
`${this.constructor.name}-init`,
|
||||
`${this.constructor.name}-init-start`,
|
||||
`${this.constructor.name}-init-end`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Performance reporting
|
||||
PerformanceMonitor.report((metrics) => {
|
||||
// Send to analytics
|
||||
if (window.gtag) {
|
||||
gtag('event', 'performance_metric', {
|
||||
custom_map: { metric_name: 'custom_metric_name' },
|
||||
metric_name: metrics.name,
|
||||
value: metrics.duration
|
||||
});
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## 🎨 Admin Interface Integration
|
||||
|
||||
### Theme System
|
||||
|
||||
```javascript
|
||||
// Admin theme management
|
||||
class AdminThemeManager {
|
||||
constructor() {
|
||||
this.themes = ['light', 'dark', 'auto'];
|
||||
this.currentTheme = localStorage.getItem('admin-theme') || 'auto';
|
||||
this.mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
this.applyTheme(this.currentTheme);
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
// Theme toggle button
|
||||
document.addEventListener('click', (e) => {
|
||||
if (e.target.matches('[data-theme-toggle]')) {
|
||||
this.toggleTheme();
|
||||
}
|
||||
});
|
||||
|
||||
// System theme changes
|
||||
this.mediaQuery.addEventListener('change', () => {
|
||||
if (this.currentTheme === 'auto') {
|
||||
this.applyTheme('auto');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
applyTheme(theme) {
|
||||
let resolvedTheme = theme;
|
||||
|
||||
if (theme === 'auto') {
|
||||
resolvedTheme = this.mediaQuery.matches ? 'dark' : 'light';
|
||||
}
|
||||
|
||||
document.documentElement.dataset.theme = resolvedTheme;
|
||||
document.documentElement.style.setProperty('--theme-preference', theme);
|
||||
|
||||
// Update theme color meta tag
|
||||
const metaThemeColor = document.querySelector('meta[name="theme-color"]');
|
||||
if (metaThemeColor) {
|
||||
const color = resolvedTheme === 'dark' ? '#1e293b' : '#ffffff';
|
||||
metaThemeColor.setAttribute('content', color);
|
||||
}
|
||||
}
|
||||
|
||||
toggleTheme() {
|
||||
const currentIndex = this.themes.indexOf(this.currentTheme);
|
||||
const nextIndex = (currentIndex + 1) % this.themes.length;
|
||||
const nextTheme = this.themes[nextIndex];
|
||||
|
||||
this.setTheme(nextTheme);
|
||||
}
|
||||
|
||||
setTheme(theme) {
|
||||
this.currentTheme = theme;
|
||||
localStorage.setItem('admin-theme', theme);
|
||||
this.applyTheme(theme);
|
||||
|
||||
events.emit('theme:changed', { theme, resolvedTheme: this.getResolvedTheme() });
|
||||
}
|
||||
|
||||
getResolvedTheme() {
|
||||
if (this.currentTheme === 'auto') {
|
||||
return this.mediaQuery.matches ? 'dark' : 'light';
|
||||
}
|
||||
return this.currentTheme;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize admin theme
|
||||
new AdminThemeManager();
|
||||
```
|
||||
|
||||
### Data Tables
|
||||
|
||||
```javascript
|
||||
// Admin data table component
|
||||
class AdminDataTable extends BaseComponent {
|
||||
defaults = {
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
paginated: true,
|
||||
pageSize: 25
|
||||
};
|
||||
|
||||
init() {
|
||||
this.table = this.element.querySelector('table');
|
||||
this.tbody = this.table.querySelector('tbody');
|
||||
this.headers = [...this.table.querySelectorAll('th[data-sort]')];
|
||||
this.filterInput = this.element.querySelector('[data-table-filter]');
|
||||
|
||||
this.data = this.extractData();
|
||||
this.filteredData = [...this.data];
|
||||
this.currentSort = { column: null, direction: 'asc' };
|
||||
this.currentPage = 1;
|
||||
|
||||
this.bindEvents();
|
||||
this.render();
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
// Column sorting
|
||||
this.headers.forEach(header => {
|
||||
header.addEventListener('click', () => {
|
||||
const column = header.dataset.sort;
|
||||
this.sort(column);
|
||||
});
|
||||
});
|
||||
|
||||
// Filtering
|
||||
if (this.filterInput) {
|
||||
this.filterInput.addEventListener('input', debounce(() => {
|
||||
this.filter(this.filterInput.value);
|
||||
}, 300));
|
||||
}
|
||||
}
|
||||
|
||||
sort(column) {
|
||||
if (this.currentSort.column === column) {
|
||||
this.currentSort.direction = this.currentSort.direction === 'asc' ? 'desc' : 'asc';
|
||||
} else {
|
||||
this.currentSort.column = column;
|
||||
this.currentSort.direction = 'asc';
|
||||
}
|
||||
|
||||
this.filteredData.sort((a, b) => {
|
||||
const aVal = a[column];
|
||||
const bVal = b[column];
|
||||
const modifier = this.currentSort.direction === 'asc' ? 1 : -1;
|
||||
|
||||
if (aVal < bVal) return -1 * modifier;
|
||||
if (aVal > bVal) return 1 * modifier;
|
||||
return 0;
|
||||
});
|
||||
|
||||
this.currentPage = 1;
|
||||
this.render();
|
||||
}
|
||||
|
||||
filter(query) {
|
||||
if (!query) {
|
||||
this.filteredData = [...this.data];
|
||||
} else {
|
||||
const searchTerm = query.toLowerCase();
|
||||
this.filteredData = this.data.filter(row => {
|
||||
return Object.values(row).some(value =>
|
||||
String(value).toLowerCase().includes(searchTerm)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
this.currentPage = 1;
|
||||
this.render();
|
||||
}
|
||||
|
||||
render() {
|
||||
// Update table body
|
||||
this.renderTableBody();
|
||||
|
||||
// Update sort indicators
|
||||
this.updateSortIndicators();
|
||||
|
||||
// Update pagination
|
||||
if (this.options.paginated) {
|
||||
this.renderPagination();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🔧 Utility Functions
|
||||
|
||||
```javascript
|
||||
// Common utility functions
|
||||
export const utils = {
|
||||
// Debounce function calls
|
||||
debounce(func, wait) {
|
||||
let timeout;
|
||||
return function executedFunction(...args) {
|
||||
const later = () => {
|
||||
clearTimeout(timeout);
|
||||
func(...args);
|
||||
};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
};
|
||||
},
|
||||
|
||||
// Throttle function calls
|
||||
throttle(func, limit) {
|
||||
let inThrottle;
|
||||
return function(...args) {
|
||||
if (!inThrottle) {
|
||||
func.apply(this, args);
|
||||
inThrottle = true;
|
||||
setTimeout(() => inThrottle = false, limit);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
// DOM manipulation helpers
|
||||
dom: {
|
||||
ready(fn) {
|
||||
if (document.readyState !== 'loading') {
|
||||
fn();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', fn);
|
||||
}
|
||||
},
|
||||
|
||||
create(tag, attributes = {}, children = []) {
|
||||
const element = document.createElement(tag);
|
||||
|
||||
Object.entries(attributes).forEach(([key, value]) => {
|
||||
if (key === 'className') {
|
||||
element.className = value;
|
||||
} else if (key.startsWith('data-')) {
|
||||
element.dataset[key.slice(5)] = value;
|
||||
} else {
|
||||
element.setAttribute(key, value);
|
||||
}
|
||||
});
|
||||
|
||||
children.forEach(child => {
|
||||
if (typeof child === 'string') {
|
||||
element.appendChild(document.createTextNode(child));
|
||||
} else {
|
||||
element.appendChild(child);
|
||||
}
|
||||
});
|
||||
|
||||
return element;
|
||||
}
|
||||
},
|
||||
|
||||
// Format utilities
|
||||
format: {
|
||||
bytes(bytes, decimals = 2) {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
const k = 1024;
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(decimals)) + ' ' + sizes[i];
|
||||
},
|
||||
|
||||
duration(ms) {
|
||||
if (ms < 1000) return `${ms}ms`;
|
||||
if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
|
||||
return `${(ms / 60000).toFixed(1)}m`;
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*For specific module documentation, see individual files in `/resources/js/docs/`*
|
||||
*For performance optimization, see [Performance Guidelines](../development/performance.md)*
|
||||
*For component integration, see [UI Components](components.md)*
|
||||
Reference in New Issue
Block a user