Implement comprehensive design system improvements
Build And Push Image / docker (push) Failing after 1m14s Details

- Created new design-system-v2.scss with modern design tokens
- Enhanced Vuetify theme configuration with refined colors
- Added professional dashboard styles component
- Improved typography, spacing, and visual hierarchy
- Implemented glass morphism effects with better contrast
- Added smooth animations and micro-interactions
- Improved responsive design for mobile devices
- Enhanced stat cards, data tables, and navigation
- Fixed color contrast issues identified in audit
- Added professional gradients and shadows

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Matt 2025-09-04 11:45:17 +02:00
parent e949df311b
commit 4ba24f8626
47 changed files with 1105 additions and 11254 deletions

View File

@ -44,7 +44,18 @@
"mcp__playwright__browser_fill_form",
"mcp__zen__debug",
"Bash(Copy-Item -Path \"Z:\\Repos\\monacousa-portal\\design-mockups\\pages\\auth\\ProfessionalLogin.vue\" -Destination \"Z:\\Repos\\monacousa-portal\\pages\\mockups\\login.vue\")",
"Bash(Remove-Item -Path \"Z:\\Repos\\monacousa-portal\\pages\\mockups\" -Recurse -Force)"
"Bash(Remove-Item -Path \"Z:\\Repos\\monacousa-portal\\pages\\mockups\" -Recurse -Force)",
"mcp__zen__analyze",
"Read(/Z:\\Repos\\monacousa-portal\\.playwright-mcp/**)",
"Read(/Z:\\Repos\\monacousa-portal\\.playwright-mcp/**)",
"Read(/Z:\\Repos\\monacousa-portal\\assets\\scss/**)",
"Bash(New-Item -Path \"Z:\\Repos\\monacousa-portal\\assets\\scss\\design-system-v2.scss\" -ItemType File -Force)",
"Read(/Z:\\Repos\\monacousa-portal\\assets\\scss/**)",
"Read(/Z:\\Repos\\monacousa-portal/**)",
"Read(/Z:\\Repos\\monacousa-portal/**)",
"Read(/Z:\\Repos\\monacousa-portal\\components\\ui/**)",
"Read(/Z:\\Repos\\monacousa-portal\\components\\ui/**)",
"Read(/Z:\\Repos\\monacousa-portal\\pages\\admin\\dashboard/**)"
],
"deny": [],
"ask": []

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 539 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 558 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 567 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 527 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 527 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 229 KiB

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 563 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 539 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 415 KiB

After

Width:  |  Height:  |  Size: 527 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 565 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

View File

@ -1,480 +0,0 @@
# MonacoUSA Portal Design System Implementation Guide
## 🎨 Overview
This comprehensive guide outlines the complete visual redesign of the MonacoUSA Portal, transitioning from a standard Vuetify implementation to a premium, custom design system featuring Monaco's signature red and white color scheme with modern glass morphism effects.
## 📋 Table of Contents
1. [Design Philosophy](#design-philosophy)
2. [Technical Stack](#technical-stack)
3. [Migration Strategy](#migration-strategy)
4. [Component Architecture](#component-architecture)
5. [Implementation Roadmap](#implementation-roadmap)
6. [Performance Guidelines](#performance-guidelines)
7. [Accessibility Standards](#accessibility-standards)
## Design Philosophy
### Core Principles
- **Premium Feel**: Every interaction should feel smooth and sophisticated
- **Brand Identity**: Monaco's red (#dc2626) as the primary accent color
- **Modern Aesthetics**: Glass morphism, subtle animations, and floating elements
- **User Experience**: Intuitive navigation with clear visual hierarchy
- **Performance**: Animations that enhance, not hinder, user experience
### Visual Language
- **Glass Morphism**: Semi-transparent surfaces with backdrop blur
- **Gradient Accents**: Dynamic gradients from Monaco red to deeper shades
- **Floating Elements**: Subtle shadows and depth for interactive components
- **Micro-animations**: Smooth transitions on hover, click, and state changes
- **Consistent Spacing**: 8px grid system for alignment and padding
## Technical Stack
### Current Setup
- **Framework**: Nuxt 3.8.2
- **UI Library**: Vuetify 3.4.7 (to be replaced)
- **Icons**: Material Design Icons (transitioning to Lucide)
- **Authentication**: Keycloak integration
- **Styling**: SCSS with scoped components
### New Additions
- **Animation Libraries**:
- VueUse Motion (preferred for Vue integration)
- GSAP (for complex animations)
- Anime.js (lightweight alternative)
- **Icons**: Lucide Icons (modern, customizable)
- **Utilities**: Tailwind CSS (for rapid prototyping)
- **State Management**: Pinia (already implemented)
## Migration Strategy
### Phase 1: Foundation (Week 1-2)
1. Set up new style architecture
2. Create base component library
3. Implement color system and typography
4. Set up animation utilities
### Phase 2: Core Components (Week 3-4)
1. Replace navigation components
2. Implement custom dropdowns and selects
3. Create button variants
4. Build card components
### Phase 3: Page Templates (Week 5-6)
1. Dashboard layouts
2. Data tables with glass effects
3. Form components
4. Modal and dialog systems
### Phase 4: Polish & Optimization (Week 7-8)
1. Performance tuning
2. Accessibility audit
3. Cross-browser testing
4. Documentation completion
## Component Architecture
### File Structure
```
components/
├── ui/ # Base UI components
│ ├── MonacoButton.vue
│ ├── GlassCard.vue
│ ├── AnimatedDropdown.vue
│ └── FloatingInput.vue
├── layout/ # Layout components
│ ├── DashboardSidebar.vue
│ ├── AppHeader.vue
│ └── PageContainer.vue
├── features/ # Feature-specific components
│ ├── MemberCard.vue
│ ├── EventCalendar.vue
│ └── DuesTracker.vue
└── shared/ # Shared utilities
├── LoadingSpinner.vue
├── ErrorBoundary.vue
└── TransitionWrapper.vue
```
### Component Guidelines
#### Naming Convention
- PascalCase for component files
- Prefix with "Monaco" for custom branded components
- Use descriptive names (e.g., `GlassDropdownMenu` not `Dropdown`)
#### Props & Events
```vue
<script setup lang="ts">
interface Props {
variant?: 'primary' | 'glass' | 'gradient'
size?: 'sm' | 'md' | 'lg'
animated?: boolean
}
const props = withDefaults(defineProps<Props>(), {
variant: 'primary',
size: 'md',
animated: true
})
const emit = defineEmits<{
click: [event: MouseEvent]
hover: [state: boolean]
}>()
</script>
```
#### Composition API Pattern
```vue
<script setup lang="ts">
import { useMonacoTheme } from '~/composables/useMonacoTheme'
import { useGlassEffect } from '~/composables/useGlassEffect'
const { primaryColor, gradients } = useMonacoTheme()
const { glassStyle, blurAmount } = useGlassEffect()
</script>
```
## Implementation Roadmap
### Week 1: Setup & Foundation
- [ ] Configure animation libraries
- [ ] Set up SCSS architecture
- [ ] Create color system utilities
- [ ] Implement base glass morphism styles
- [ ] Set up Lucide icons integration
### Week 2: Core Components
- [ ] Custom dropdown components
- [ ] Button system with variants
- [ ] Card components with glass effects
- [ ] Form inputs with floating labels
- [ ] Navigation components
### Week 3: Dashboard Implementation
- [ ] Member dashboard layout
- [ ] Board dashboard enhancements
- [ ] Admin panel structure
- [ ] Widget components
- [ ] Chart integrations
### Week 4: Advanced Features
- [ ] Event calendar with animations
- [ ] Member management interface
- [ ] Dues payment flow
- [ ] Profile components
- [ ] Settings panels
### Week 5: Responsive Design
- [ ] Mobile navigation
- [ ] Tablet optimizations
- [ ] Touch interactions
- [ ] Gesture support
- [ ] PWA enhancements
### Week 6: Testing & Optimization
- [ ] Performance profiling
- [ ] Bundle optimization
- [ ] Lazy loading implementation
- [ ] Animation performance tuning
- [ ] Memory leak detection
## Performance Guidelines
### Animation Performance
```scss
// Use transform and opacity for animations
.animated-element {
will-change: transform, opacity;
transform: translateZ(0); // Enable hardware acceleration
}
// Avoid animating expensive properties
// BAD: width, height, padding, margin
// GOOD: transform, opacity, filter
```
### Component Optimization
```vue
<!-- Use v-show for frequently toggled elements -->
<div v-show="isVisible" class="glass-panel">
<!-- Content -->
</div>
<!-- Use v-if for conditionally rendered heavy components -->
<HeavyComponent v-if="shouldRender" />
<!-- Implement lazy loading for routes -->
<script setup>
const MemberDashboard = defineAsyncComponent(() =>
import('~/components/dashboards/MemberDashboard.vue')
)
</script>
```
### Bundle Size Management
- Tree-shake unused Vuetify components
- Lazy load animation libraries
- Use dynamic imports for heavy features
- Implement code splitting by route
- Optimize images with next-gen formats
## Accessibility Standards
### WCAG 2.1 Compliance
- **Color Contrast**: Ensure 4.5:1 ratio for normal text
- **Focus Indicators**: Clear visual focus states
- **Keyboard Navigation**: Full keyboard support
- **Screen Readers**: Proper ARIA labels
- **Motion Sensitivity**: Respect prefers-reduced-motion
### Implementation Examples
```vue
<template>
<button
:aria-label="ariaLabel"
:aria-pressed="isPressed"
@click="handleClick"
@keydown.enter="handleClick"
@keydown.space.prevent="handleClick"
class="monaco-button"
:class="{ 'reduced-motion': prefersReducedMotion }"
>
<slot />
</button>
</template>
<script setup>
const prefersReducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)')
</script>
<style scoped>
.monaco-button {
transition: all 0.3s ease;
}
.monaco-button.reduced-motion {
transition: none;
}
</style>
```
## Style Guidelines
### Color System
```scss
// Primary Colors
$monaco-red: #dc2626;
$monaco-red-dark: #b91c1c;
$monaco-red-light: #ef4444;
$monaco-white: #ffffff;
// Gradients
$monaco-gradient: linear-gradient(135deg, $monaco-red 0%, $monaco-red-dark 100%);
$monaco-gradient-reverse: linear-gradient(135deg, $monaco-red-light 0%, $monaco-red 100%);
// Glass Effects
$glass-bg: rgba(255, 255, 255, 0.7);
$glass-border: rgba(255, 255, 255, 0.3);
$glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
$glass-blur: 20px;
```
### Typography Scale
```scss
// Font Family
$font-primary: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
$font-mono: 'Fira Code', 'Monaco', monospace;
// Font Sizes
$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
```
### Spacing System
```scss
// Based on 8px grid
$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
```
## Component Examples
### Glass Card Component
```vue
<template>
<div class="glass-card" :class="[sizeClass, variantClass]">
<div v-if="hasHeader" class="glass-card-header">
<slot name="header" />
</div>
<div class="glass-card-body">
<slot />
</div>
<div v-if="hasFooter" class="glass-card-footer">
<slot name="footer" />
</div>
</div>
</template>
<script setup lang="ts">
interface Props {
size?: 'sm' | 'md' | 'lg'
variant?: 'light' | 'dark' | 'colored'
}
const props = withDefaults(defineProps<Props>(), {
size: 'md',
variant: 'light'
})
const slots = useSlots()
const hasHeader = computed(() => !!slots.header)
const hasFooter = computed(() => !!slots.footer)
const sizeClass = computed(() => `glass-card--${props.size}`)
const variantClass = computed(() => `glass-card--${props.variant}`)
</script>
<style scoped lang="scss">
.glass-card {
background: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(20px);
border-radius: 16px;
border: 1px solid rgba(255, 255, 255, 0.3);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
overflow: hidden;
transition: all 0.3s ease;
&:hover {
transform: translateY(-2px);
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
}
&--dark {
background: rgba(0, 0, 0, 0.7);
border-color: rgba(255, 255, 255, 0.1);
}
&--colored {
background: linear-gradient(135deg,
rgba(220, 38, 38, 0.1) 0%,
rgba(185, 28, 28, 0.1) 100%);
}
}
</style>
```
## Testing Strategy
### Unit Testing
```typescript
// components/__tests__/MonacoButton.spec.ts
import { mount } from '@vue/test-utils'
import MonacoButton from '~/components/ui/MonacoButton.vue'
describe('MonacoButton', () => {
it('renders with correct variant class', () => {
const wrapper = mount(MonacoButton, {
props: { variant: 'gradient' }
})
expect(wrapper.classes()).toContain('monaco-button--gradient')
})
it('emits click event', async () => {
const wrapper = mount(MonacoButton)
await wrapper.trigger('click')
expect(wrapper.emitted('click')).toHaveLength(1)
})
})
```
### E2E Testing
```typescript
// e2e/dashboard.spec.ts
import { test, expect } from '@playwright/test'
test('dashboard loads with glass morphism effects', async ({ page }) => {
await page.goto('/dashboard')
const glassCard = page.locator('.glass-card').first()
await expect(glassCard).toBeVisible()
const styles = await glassCard.evaluate(el =>
window.getComputedStyle(el)
)
expect(styles.backdropFilter).toContain('blur')
})
```
## Deployment Checklist
### Pre-deployment
- [ ] Run full test suite
- [ ] Check bundle size (<500KB initial)
- [ ] Validate accessibility scores
- [ ] Test on all target browsers
- [ ] Optimize images and assets
- [ ] Review security headers
### Performance Metrics
- First Contentful Paint: <1.5s
- Time to Interactive: <3s
- Cumulative Layout Shift: <0.1
- First Input Delay: <100ms
- Lighthouse Score: >90
### Browser Support
- Chrome 90+
- Firefox 88+
- Safari 14+
- Edge 90+
- Mobile Safari 14+
- Chrome Mobile 90+
## Resources
### Documentation
- [Vue 3 Composition API](https://vuejs.org/guide/extras/composition-api-faq.html)
- [Nuxt 3 Documentation](https://nuxt.com/docs)
- [VueUse Motion](https://motion.vueuse.org/)
- [GSAP Documentation](https://greensock.com/docs/)
- [Lucide Icons](https://lucide.dev/)
### Design Inspiration
- [Glass Morphism Examples](https://glassmorphism.com/)
- [Monaco Brand Guidelines](internal-link)
- [Material Design 3](https://m3.material.io/)
### Tools
- [Contrast Checker](https://webaim.org/resources/contrastchecker/)
- [Bundle Analyzer](https://github.com/nuxt/devtools)
- [Lighthouse CI](https://github.com/GoogleChrome/lighthouse-ci)
## Support
For questions or assistance with implementation:
- Technical Lead: [Contact Info]
- Design Team: [Contact Info]
- Documentation: This guide and `/Design` folder
---
*Last Updated: December 2024*
*Version: 1.0.0*

View File

@ -1,510 +0,0 @@
<template>
<div class="animated-select" ref="selectRef">
<button
@click="toggleSelect"
class="animated-select__trigger"
:class="{ 'animated-select__trigger--open': isOpen }"
>
<div class="animated-select__display">
<Transition name="slide-fade" mode="out-in">
<span
v-if="!selectedOption"
key="placeholder"
class="animated-select__placeholder"
>
{{ placeholder }}
</span>
<div
v-else
key="selected"
class="animated-select__selected"
>
<Icon
v-if="selectedOption.icon"
:name="selectedOption.icon"
class="animated-select__icon"
/>
<span>{{ selectedOption.label }}</span>
</div>
</Transition>
</div>
<div class="animated-select__arrow">
<svg
class="animated-select__arrow-icon"
:class="{ 'animated-select__arrow-icon--rotate': isOpen }"
width="20"
height="20"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
/>
</svg>
</div>
</button>
<Teleport to="body">
<Transition name="select-dropdown">
<div
v-if="isOpen"
class="animated-select__dropdown"
:style="dropdownStyle"
@click.stop
>
<div class="animated-select__options">
<TransitionGroup name="option-list">
<div
v-for="(option, index) in options"
:key="option.value"
class="animated-select__option"
:class="{
'animated-select__option--selected': modelValue === option.value,
'animated-select__option--highlighted': highlightedIndex === index,
'animated-select__option--disabled': option.disabled
}"
:style="{ '--delay': `${index * 30}ms` }"
@click="!option.disabled && selectOption(option)"
@mouseenter="highlightedIndex = index"
@mouseleave="highlightedIndex = -1"
>
<div class="animated-select__option-content">
<Icon
v-if="option.icon"
:name="option.icon"
class="animated-select__option-icon"
/>
<span class="animated-select__option-label">
{{ option.label }}
</span>
<span
v-if="option.description"
class="animated-select__option-description"
>
{{ option.description }}
</span>
</div>
<Transition name="check">
<Icon
v-if="modelValue === option.value"
name="check"
class="animated-select__option-check"
/>
</Transition>
</div>
</TransitionGroup>
</div>
</div>
</Transition>
</Teleport>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'
import Icon from '~/components/ui/Icon.vue'
interface SelectOption {
label: string
value: string | number
icon?: string
description?: string
disabled?: boolean
}
interface Props {
modelValue?: string | number | null
options: SelectOption[]
placeholder?: string
searchable?: boolean
multiple?: boolean
}
const props = withDefaults(defineProps<Props>(), {
placeholder: 'Select an option',
searchable: false,
multiple: false
})
const emit = defineEmits<{
'update:modelValue': [value: string | number | null]
'change': [value: string | number | null]
'open': []
'close': []
}>()
const selectRef = ref<HTMLElement>()
const isOpen = ref(false)
const highlightedIndex = ref(-1)
const dropdownStyle = ref({})
const selectedOption = computed(() => {
return props.options.find(opt => opt.value === props.modelValue)
})
const toggleSelect = () => {
isOpen.value = !isOpen.value
if (isOpen.value) {
emit('open')
nextTick(() => updateDropdownPosition())
} else {
emit('close')
highlightedIndex.value = -1
}
}
const selectOption = (option: SelectOption) => {
emit('update:modelValue', option.value)
emit('change', option.value)
isOpen.value = false
highlightedIndex.value = -1
emit('close')
}
const updateDropdownPosition = () => {
if (!selectRef.value) return
const rect = selectRef.value.getBoundingClientRect()
const spaceBelow = window.innerHeight - rect.bottom
const spaceAbove = rect.top
const dropdownHeight = 300 // Approximate max height
let top = rect.bottom + 8
if (spaceBelow < dropdownHeight && spaceAbove > spaceBelow) {
top = rect.top - dropdownHeight - 8
}
dropdownStyle.value = {
position: 'fixed',
top: `${top}px`,
left: `${rect.left}px`,
width: `${rect.width}px`,
zIndex: 9999
}
}
const handleClickOutside = (event: MouseEvent) => {
if (selectRef.value && !selectRef.value.contains(event.target as Node)) {
isOpen.value = false
highlightedIndex.value = -1
emit('close')
}
}
const handleEscape = (event: KeyboardEvent) => {
if (event.key === 'Escape' && isOpen.value) {
isOpen.value = false
highlightedIndex.value = -1
emit('close')
}
}
const handleKeyNavigation = (event: KeyboardEvent) => {
if (!isOpen.value) return
switch (event.key) {
case 'ArrowDown':
event.preventDefault()
highlightedIndex.value = Math.min(
highlightedIndex.value + 1,
props.options.length - 1
)
break
case 'ArrowUp':
event.preventDefault()
highlightedIndex.value = Math.max(highlightedIndex.value - 1, 0)
break
case 'Enter':
event.preventDefault()
if (highlightedIndex.value >= 0) {
const option = props.options[highlightedIndex.value]
if (!option.disabled) {
selectOption(option)
}
}
break
}
}
onMounted(() => {
document.addEventListener('click', handleClickOutside)
document.addEventListener('keydown', handleEscape)
document.addEventListener('keydown', handleKeyNavigation)
window.addEventListener('resize', updateDropdownPosition)
window.addEventListener('scroll', updateDropdownPosition)
})
onUnmounted(() => {
document.removeEventListener('click', handleClickOutside)
document.removeEventListener('keydown', handleEscape)
document.removeEventListener('keydown', handleKeyNavigation)
window.removeEventListener('resize', updateDropdownPosition)
window.removeEventListener('scroll', updateDropdownPosition)
})
watch(isOpen, (newVal) => {
if (newVal) {
nextTick(() => updateDropdownPosition())
}
})
</script>
<style scoped lang="scss">
.animated-select {
position: relative;
width: 100%;
&__trigger {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 0.75rem 1rem;
background: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 2px solid transparent;
border-radius: 16px;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
outline: none;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
&:hover {
background: rgba(255, 255, 255, 0.8);
border-color: rgba(220, 38, 38, 0.2);
box-shadow: 0 6px 24px rgba(0, 0, 0, 0.08);
transform: translateY(-1px);
}
&:focus {
border-color: #dc2626;
box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.1);
}
&--open {
border-color: #dc2626;
background: rgba(255, 255, 255, 0.9);
box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.1);
}
}
&__display {
flex: 1;
min-height: 1.5rem;
}
&__placeholder {
color: #71717a;
font-size: 0.9375rem;
}
&__selected {
display: flex;
align-items: center;
gap: 0.5rem;
color: #27272a;
font-weight: 500;
}
&__icon {
width: 1.25rem;
height: 1.25rem;
color: #dc2626;
}
&__arrow {
display: flex;
align-items: center;
justify-content: center;
width: 1.5rem;
height: 1.5rem;
}
&__arrow-icon {
color: #dc2626;
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
&--rotate {
transform: rotate(180deg);
}
}
&__dropdown {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(30px);
-webkit-backdrop-filter: blur(30px);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 16px;
box-shadow:
0 20px 40px rgba(0, 0, 0, 0.1),
0 0 0 1px rgba(220, 38, 38, 0.05);
overflow: hidden;
max-height: 300px;
overflow-y: auto;
&::-webkit-scrollbar {
width: 8px;
}
&::-webkit-scrollbar-track {
background: transparent;
}
&::-webkit-scrollbar-thumb {
background: rgba(220, 38, 38, 0.2);
border-radius: 4px;
&:hover {
background: rgba(220, 38, 38, 0.3);
}
}
}
&__options {
padding: 0.25rem;
}
&__option {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.75rem 1rem;
margin: 0.125rem 0;
border-radius: 12px;
cursor: pointer;
transition: all 0.2s ease;
animation: slideIn 0.3s cubic-bezier(0.4, 0, 0.2, 1) both;
animation-delay: var(--delay);
&:hover:not(&--disabled) {
background: linear-gradient(135deg,
rgba(220, 38, 38, 0.05) 0%,
rgba(220, 38, 38, 0.1) 100%);
transform: translateX(4px);
}
&--highlighted {
background: rgba(220, 38, 38, 0.05);
}
&--selected {
background: linear-gradient(135deg,
rgba(220, 38, 38, 0.1) 0%,
rgba(220, 38, 38, 0.15) 100%);
color: #dc2626;
font-weight: 600;
}
&--disabled {
opacity: 0.4;
cursor: not-allowed;
}
}
&__option-content {
display: flex;
align-items: center;
gap: 0.75rem;
flex: 1;
}
&__option-icon {
width: 1.25rem;
height: 1.25rem;
color: #dc2626;
}
&__option-label {
font-size: 0.9375rem;
color: inherit;
}
&__option-description {
font-size: 0.75rem;
color: #71717a;
margin-left: auto;
margin-right: 1rem;
}
&__option-check {
width: 1.25rem;
height: 1.25rem;
color: #dc2626;
}
}
// Animations
@keyframes slideIn {
from {
opacity: 0;
transform: translateX(-10px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.slide-fade-enter-active,
.slide-fade-leave-active {
transition: all 0.2s ease;
}
.slide-fade-enter-from {
opacity: 0;
transform: translateY(-4px);
}
.slide-fade-leave-to {
opacity: 0;
transform: translateY(4px);
}
.select-dropdown-enter-active,
.select-dropdown-leave-active {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.select-dropdown-enter-from {
opacity: 0;
transform: scale(0.95) translateY(-10px);
filter: blur(4px);
}
.select-dropdown-leave-to {
opacity: 0;
transform: scale(0.95) translateY(-10px);
filter: blur(4px);
}
.option-list-enter-active,
.option-list-leave-active {
transition: all 0.3s ease;
}
.option-list-enter-from {
opacity: 0;
transform: translateX(-20px);
}
.option-list-leave-to {
opacity: 0;
transform: translateX(20px);
}
.check-enter-active,
.check-leave-active {
transition: all 0.2s ease;
}
.check-enter-from,
.check-leave-to {
opacity: 0;
transform: scale(0.5);
}
</style>

View File

@ -1,713 +0,0 @@
<template>
<div class="glass-dropdown" ref="dropdownRef">
<div
class="glass-dropdown__trigger"
@click="toggleDropdown"
:class="{ 'glass-dropdown__trigger--active': isOpen }"
>
<div class="glass-dropdown__trigger-content">
<slot name="trigger">
<div class="glass-dropdown__trigger-default">
<Icon
v-if="icon"
:name="icon"
class="glass-dropdown__trigger-icon"
/>
<span class="glass-dropdown__trigger-text">{{ label }}</span>
<div class="glass-dropdown__trigger-arrow">
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
class="glass-dropdown__arrow-svg"
:class="{ 'glass-dropdown__arrow-svg--rotate': isOpen }"
>
<path
d="M4 6L8 10L12 6"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</div>
</div>
</slot>
</div>
<div class="glass-dropdown__trigger-glow"></div>
</div>
<Teleport to="body">
<Transition name="glass-fade">
<div
v-if="isOpen"
class="glass-dropdown__backdrop"
@click="closeDropdown"
>
<div
class="glass-dropdown__menu"
:style="menuStyle"
@click.stop
>
<div class="glass-dropdown__menu-inner">
<div
v-if="$slots.header"
class="glass-dropdown__header"
>
<slot name="header"></slot>
</div>
<div class="glass-dropdown__items">
<template v-if="groups.length > 0">
<div
v-for="(group, groupIndex) in groups"
:key="groupIndex"
class="glass-dropdown__group"
>
<div
v-if="group.label"
class="glass-dropdown__group-label"
>
{{ group.label }}
</div>
<div
v-for="(item, itemIndex) in group.items"
:key="`${groupIndex}-${itemIndex}`"
class="glass-dropdown__item"
:class="{
'glass-dropdown__item--active': isItemActive(item),
'glass-dropdown__item--disabled': item.disabled,
'glass-dropdown__item--danger': item.variant === 'danger'
}"
@click="!item.disabled && handleItemClick(item)"
@mouseenter="hoveredItem = item"
@mouseleave="hoveredItem = null"
>
<div class="glass-dropdown__item-content">
<Icon
v-if="item.icon"
:name="item.icon"
class="glass-dropdown__item-icon"
/>
<div class="glass-dropdown__item-text">
<div class="glass-dropdown__item-label">
{{ item.label }}
</div>
<div
v-if="item.description"
class="glass-dropdown__item-description"
>
{{ item.description }}
</div>
</div>
<div
v-if="item.badge"
class="glass-dropdown__item-badge"
>
{{ item.badge }}
</div>
<Icon
v-if="item.submenu"
name="chevron-right"
class="glass-dropdown__item-chevron"
/>
</div>
<Transition name="submenu-slide">
<div
v-if="item.submenu && hoveredItem === item"
class="glass-dropdown__submenu"
>
<div
v-for="(subItem, subIndex) in item.submenu"
:key="subIndex"
class="glass-dropdown__submenu-item"
:class="{
'glass-dropdown__submenu-item--disabled': subItem.disabled
}"
@click.stop="!subItem.disabled && handleSubmenuClick(subItem)"
>
<Icon
v-if="subItem.icon"
:name="subItem.icon"
class="glass-dropdown__submenu-icon"
/>
<span>{{ subItem.label }}</span>
</div>
</div>
</Transition>
</div>
<div
v-if="groupIndex < groups.length - 1"
class="glass-dropdown__divider"
></div>
</div>
</template>
<div v-else class="glass-dropdown__empty">
<slot name="empty">
<p>No items available</p>
</slot>
</div>
</div>
<div
v-if="$slots.footer"
class="glass-dropdown__footer"
>
<slot name="footer"></slot>
</div>
</div>
<div class="glass-dropdown__menu-glow"></div>
</div>
</div>
</Transition>
</Teleport>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'
import Icon from '~/components/ui/Icon.vue'
interface DropdownItem {
label: string
value?: string | number
icon?: string
description?: string
badge?: string | number
variant?: 'default' | 'danger'
disabled?: boolean
action?: () => void
submenu?: DropdownItem[]
}
interface DropdownGroup {
label?: string
items: DropdownItem[]
}
interface Props {
label?: string
icon?: string
groups?: DropdownGroup[]
items?: DropdownItem[]
modelValue?: string | number | null
closeOnSelect?: boolean
}
const props = withDefaults(defineProps<Props>(), {
label: 'Menu',
closeOnSelect: true
})
const emit = defineEmits<{
'update:modelValue': [value: string | number | null]
'select': [item: DropdownItem]
'open': []
'close': []
}>()
const dropdownRef = ref<HTMLElement>()
const isOpen = ref(false)
const hoveredItem = ref<DropdownItem | null>(null)
const menuStyle = ref({})
const groups = computed(() => {
if (props.groups && props.groups.length > 0) {
return props.groups
}
if (props.items && props.items.length > 0) {
return [{ items: props.items }]
}
return []
})
const toggleDropdown = () => {
isOpen.value = !isOpen.value
if (isOpen.value) {
emit('open')
nextTick(() => updateMenuPosition())
} else {
emit('close')
hoveredItem.value = null
}
}
const closeDropdown = () => {
isOpen.value = false
hoveredItem.value = null
emit('close')
}
const handleItemClick = (item: DropdownItem) => {
if (item.action) {
item.action()
}
if (item.value !== undefined) {
emit('update:modelValue', item.value)
}
emit('select', item)
if (props.closeOnSelect && !item.submenu) {
closeDropdown()
}
}
const handleSubmenuClick = (item: DropdownItem) => {
if (item.action) {
item.action()
}
emit('select', item)
if (props.closeOnSelect) {
closeDropdown()
}
}
const isItemActive = (item: DropdownItem) => {
return item.value !== undefined && item.value === props.modelValue
}
const updateMenuPosition = () => {
if (!dropdownRef.value) return
const rect = dropdownRef.value.getBoundingClientRect()
const windowWidth = window.innerWidth
const windowHeight = window.innerHeight
let top = rect.bottom + 8
let left = rect.left
// Adjust if menu would go off screen
const menuWidth = 320 // Approximate menu width
const menuHeight = 400 // Approximate max menu height
if (left + menuWidth > windowWidth) {
left = windowWidth - menuWidth - 16
}
if (top + menuHeight > windowHeight && rect.top > menuHeight) {
top = rect.top - menuHeight - 8
}
menuStyle.value = {
position: 'fixed',
top: `${top}px`,
left: `${left}px`,
minWidth: `${rect.width}px`,
zIndex: 10001
}
}
const handleEscape = (event: KeyboardEvent) => {
if (event.key === 'Escape' && isOpen.value) {
closeDropdown()
}
}
onMounted(() => {
document.addEventListener('keydown', handleEscape)
window.addEventListener('resize', updateMenuPosition)
window.addEventListener('scroll', updateMenuPosition)
})
onUnmounted(() => {
document.removeEventListener('keydown', handleEscape)
window.removeEventListener('resize', updateMenuPosition)
window.removeEventListener('scroll', updateMenuPosition)
})
</script>
<style scoped lang="scss">
.glass-dropdown {
position: relative;
display: inline-block;
&__trigger {
position: relative;
display: inline-flex;
align-items: center;
padding: 0.625rem 1rem;
background: rgba(255, 255, 255, 0.6);
backdrop-filter: blur(30px) saturate(180%);
-webkit-backdrop-filter: blur(30px) saturate(180%);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 14px;
cursor: pointer;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow:
0 4px 20px rgba(0, 0, 0, 0.04),
inset 0 1px 0 rgba(255, 255, 255, 0.8);
&:hover {
background: rgba(255, 255, 255, 0.7);
border-color: rgba(220, 38, 38, 0.3);
transform: translateY(-2px);
box-shadow:
0 8px 30px rgba(220, 38, 38, 0.1),
inset 0 1px 0 rgba(255, 255, 255, 0.9);
.glass-dropdown__trigger-glow {
opacity: 1;
}
}
&--active {
background: rgba(255, 255, 255, 0.8);
border-color: rgba(220, 38, 38, 0.4);
box-shadow:
0 0 0 4px rgba(220, 38, 38, 0.1),
0 8px 30px rgba(220, 38, 38, 0.15),
inset 0 1px 0 rgba(255, 255, 255, 1);
}
}
&__trigger-content {
position: relative;
z-index: 1;
}
&__trigger-default {
display: flex;
align-items: center;
gap: 0.5rem;
}
&__trigger-icon {
width: 1.25rem;
height: 1.25rem;
color: #dc2626;
}
&__trigger-text {
font-weight: 500;
color: #27272a;
font-size: 0.9375rem;
}
&__trigger-arrow {
margin-left: 0.5rem;
}
&__arrow-svg {
color: #dc2626;
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
&--rotate {
transform: rotate(180deg);
}
}
&__trigger-glow {
position: absolute;
inset: -1px;
background: linear-gradient(135deg,
rgba(220, 38, 38, 0.2) 0%,
rgba(220, 38, 38, 0) 100%);
border-radius: 14px;
opacity: 0;
transition: opacity 0.4s ease;
pointer-events: none;
}
&__backdrop {
position: fixed;
inset: 0;
z-index: 10000;
background: rgba(0, 0, 0, 0.2);
backdrop-filter: blur(4px);
}
&__menu {
position: absolute;
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(40px) saturate(200%);
-webkit-backdrop-filter: blur(40px) saturate(200%);
border: 1px solid rgba(255, 255, 255, 0.4);
border-radius: 20px;
box-shadow:
0 20px 60px rgba(0, 0, 0, 0.15),
0 0 0 1px rgba(220, 38, 38, 0.05),
inset 0 1px 0 rgba(255, 255, 255, 0.9);
overflow: hidden;
min-width: 280px;
max-width: 400px;
max-height: 70vh;
overflow-y: auto;
&::-webkit-scrollbar {
width: 10px;
}
&::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.05);
border-radius: 10px;
}
&::-webkit-scrollbar-thumb {
background: linear-gradient(180deg,
rgba(220, 38, 38, 0.3) 0%,
rgba(220, 38, 38, 0.1) 100%);
border-radius: 10px;
border: 2px solid rgba(255, 255, 255, 0.9);
&:hover {
background: linear-gradient(180deg,
rgba(220, 38, 38, 0.4) 0%,
rgba(220, 38, 38, 0.2) 100%);
}
}
}
&__menu-inner {
position: relative;
z-index: 1;
padding: 0.5rem;
}
&__menu-glow {
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: radial-gradient(
ellipse at center,
rgba(220, 38, 38, 0.1) 0%,
transparent 70%
);
pointer-events: none;
}
&__header {
padding: 1rem;
border-bottom: 1px solid rgba(220, 38, 38, 0.1);
margin-bottom: 0.5rem;
}
&__footer {
padding: 1rem;
border-top: 1px solid rgba(220, 38, 38, 0.1);
margin-top: 0.5rem;
}
&__group {
margin: 0.25rem 0;
}
&__group-label {
padding: 0.5rem 0.75rem;
font-size: 0.75rem;
font-weight: 600;
color: #dc2626;
text-transform: uppercase;
letter-spacing: 0.05em;
}
&__divider {
height: 1px;
background: linear-gradient(90deg,
transparent 0%,
rgba(220, 38, 38, 0.2) 50%,
transparent 100%);
margin: 0.5rem 0;
}
&__item {
position: relative;
padding: 0.625rem 0.75rem;
margin: 0.125rem 0;
border-radius: 12px;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
&:hover:not(&--disabled) {
background: linear-gradient(135deg,
rgba(220, 38, 38, 0.08) 0%,
rgba(220, 38, 38, 0.04) 100%);
transform: translateX(4px);
.glass-dropdown__item-icon {
transform: scale(1.1);
}
}
&--active {
background: linear-gradient(135deg,
rgba(220, 38, 38, 0.15) 0%,
rgba(220, 38, 38, 0.08) 100%);
color: #dc2626;
font-weight: 600;
&::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 3px;
height: 60%;
background: #dc2626;
border-radius: 0 3px 3px 0;
}
}
&--danger {
color: #ef4444;
&:hover {
background: rgba(239, 68, 68, 0.1);
}
}
&--disabled {
opacity: 0.4;
cursor: not-allowed;
}
}
&__item-content {
display: flex;
align-items: center;
gap: 0.75rem;
}
&__item-icon {
width: 1.25rem;
height: 1.25rem;
color: #dc2626;
flex-shrink: 0;
transition: transform 0.3s ease;
}
&__item-text {
flex: 1;
}
&__item-label {
font-size: 0.9375rem;
color: inherit;
}
&__item-description {
font-size: 0.75rem;
color: #71717a;
margin-top: 0.125rem;
}
&__item-badge {
padding: 0.125rem 0.5rem;
background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%);
color: white;
font-size: 0.75rem;
font-weight: 600;
border-radius: 999px;
}
&__item-chevron {
width: 1rem;
height: 1rem;
color: #71717a;
margin-left: auto;
}
&__submenu {
position: absolute;
left: calc(100% + 0.5rem);
top: 0;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(30px);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
padding: 0.25rem;
min-width: 200px;
z-index: 10;
}
&__submenu-item {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 0.75rem;
border-radius: 8px;
cursor: pointer;
transition: all 0.2s ease;
font-size: 0.875rem;
&:hover:not(&--disabled) {
background: rgba(220, 38, 38, 0.1);
color: #dc2626;
}
&--disabled {
opacity: 0.4;
cursor: not-allowed;
}
}
&__submenu-icon {
width: 1rem;
height: 1rem;
color: #dc2626;
}
&__empty {
padding: 2rem;
text-align: center;
color: #71717a;
font-size: 0.875rem;
}
}
// Animations
.glass-fade-enter-active,
.glass-fade-leave-active {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.glass-fade-enter-from {
opacity: 0;
.glass-dropdown__menu {
transform: scale(0.9) translateY(-20px);
filter: blur(10px);
}
}
.glass-fade-leave-to {
opacity: 0;
.glass-dropdown__menu {
transform: scale(0.9) translateY(-20px);
filter: blur(10px);
}
}
.submenu-slide-enter-active,
.submenu-slide-leave-active {
transition: all 0.2s ease;
}
.submenu-slide-enter-from {
opacity: 0;
transform: translateX(-10px);
}
.submenu-slide-leave-to {
opacity: 0;
transform: translateX(-10px);
}
</style>

View File

@ -1,391 +0,0 @@
<template>
<div class="monaco-dropdown" ref="dropdownRef">
<button
@click="toggleDropdown"
class="monaco-dropdown__trigger"
:class="[
`monaco-dropdown__trigger--${variant}`,
`monaco-dropdown__trigger--${size}`,
{ 'monaco-dropdown__trigger--open': isOpen }
]"
:aria-expanded="isOpen"
:aria-haspopup="true"
>
<slot name="trigger">
<span class="monaco-dropdown__trigger-text">{{ label }}</span>
</slot>
<Icon
:name="isOpen ? 'chevron-up' : 'chevron-down'"
class="monaco-dropdown__trigger-icon"
:class="{ 'monaco-dropdown__trigger-icon--rotate': isOpen }"
/>
</button>
<Transition name="dropdown">
<div
v-if="isOpen"
class="monaco-dropdown__content"
:class="[
`monaco-dropdown__content--${variant}`,
`monaco-dropdown__content--${position}`
]"
>
<div class="monaco-dropdown__content-inner">
<div
v-for="(option, index) in options"
:key="option.value || index"
class="monaco-dropdown__item"
:class="{
'monaco-dropdown__item--active': activeIndex === index,
'monaco-dropdown__item--selected': modelValue === option.value,
'monaco-dropdown__item--disabled': option.disabled
}"
@click="!option.disabled && selectOption(option)"
@mouseenter="activeIndex = index"
@mouseleave="activeIndex = -1"
>
<Icon
v-if="option.icon"
:name="option.icon"
class="monaco-dropdown__item-icon"
/>
<span class="monaco-dropdown__item-label">{{ option.label }}</span>
<span v-if="option.shortcut" class="monaco-dropdown__item-shortcut">
{{ option.shortcut }}
</span>
<Icon
v-if="modelValue === option.value"
name="check"
class="monaco-dropdown__item-check"
/>
</div>
</div>
</div>
</Transition>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted } from 'vue'
import Icon from '~/components/ui/Icon.vue'
interface DropdownOption {
label: string
value: string | number
icon?: string
shortcut?: string
disabled?: boolean
}
interface Props {
modelValue?: string | number | null
options: DropdownOption[]
label?: string
variant?: 'glass' | 'solid' | 'gradient' | 'outline'
size?: 'sm' | 'md' | 'lg'
position?: 'bottom' | 'top' | 'left' | 'right'
closeOnSelect?: boolean
}
const props = withDefaults(defineProps<Props>(), {
label: 'Select option',
variant: 'glass',
size: 'md',
position: 'bottom',
closeOnSelect: true
})
const emit = defineEmits<{
'update:modelValue': [value: string | number]
'change': [value: string | number]
'open': []
'close': []
}>()
const dropdownRef = ref<HTMLElement>()
const isOpen = ref(false)
const activeIndex = ref(-1)
const toggleDropdown = () => {
isOpen.value = !isOpen.value
if (isOpen.value) {
emit('open')
} else {
emit('close')
activeIndex.value = -1
}
}
const selectOption = (option: DropdownOption) => {
emit('update:modelValue', option.value)
emit('change', option.value)
if (props.closeOnSelect) {
isOpen.value = false
activeIndex.value = -1
emit('close')
}
}
const handleClickOutside = (event: MouseEvent) => {
if (dropdownRef.value && !dropdownRef.value.contains(event.target as Node)) {
isOpen.value = false
activeIndex.value = -1
emit('close')
}
}
const handleEscape = (event: KeyboardEvent) => {
if (event.key === 'Escape' && isOpen.value) {
isOpen.value = false
activeIndex.value = -1
emit('close')
}
}
onMounted(() => {
document.addEventListener('click', handleClickOutside)
document.addEventListener('keydown', handleEscape)
})
onUnmounted(() => {
document.removeEventListener('click', handleClickOutside)
document.removeEventListener('keydown', handleEscape)
})
</script>
<style scoped lang="scss">
.monaco-dropdown {
position: relative;
display: inline-block;
&__trigger {
display: inline-flex;
align-items: center;
justify-content: space-between;
gap: 0.5rem;
font-weight: 500;
border-radius: 12px;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
outline: none;
border: none;
&--glass {
background: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.3);
color: #dc2626;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
&:hover {
background: rgba(255, 255, 255, 0.8);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
transform: translateY(-1px);
}
&:active {
transform: translateY(0);
}
}
&--solid {
background: #dc2626;
color: white;
box-shadow: 0 4px 16px rgba(220, 38, 38, 0.2);
&:hover {
background: #b91c1c;
box-shadow: 0 6px 20px rgba(220, 38, 38, 0.3);
transform: translateY(-1px);
}
}
&--gradient {
background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%);
color: white;
box-shadow: 0 4px 16px rgba(220, 38, 38, 0.2);
&:hover {
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
box-shadow: 0 6px 20px rgba(220, 38, 38, 0.3);
transform: translateY(-1px);
}
}
&--outline {
background: transparent;
color: #dc2626;
border: 2px solid #dc2626;
&:hover {
background: rgba(220, 38, 38, 0.1);
border-color: #b91c1c;
}
}
&--sm {
padding: 0.375rem 0.75rem;
font-size: 0.875rem;
}
&--md {
padding: 0.5rem 1rem;
font-size: 1rem;
}
&--lg {
padding: 0.75rem 1.25rem;
font-size: 1.125rem;
}
&--open {
z-index: 10;
}
}
&__trigger-icon {
width: 1.25rem;
height: 1.25rem;
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
&--rotate {
transform: rotate(180deg);
}
}
&__content {
position: absolute;
z-index: 50;
min-width: 200px;
margin-top: 0.5rem;
padding: 0.25rem;
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15);
&--glass {
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.3);
}
&--solid {
background: white;
border: 1px solid #e5e5e5;
}
&--gradient {
background: linear-gradient(135deg,
rgba(255, 255, 255, 0.95) 0%,
rgba(255, 255, 255, 0.85) 100%);
backdrop-filter: blur(20px);
border: 1px solid rgba(220, 38, 38, 0.1);
}
&--outline {
background: white;
border: 2px solid #dc2626;
}
&--bottom {
top: 100%;
left: 0;
right: 0;
}
&--top {
bottom: 100%;
left: 0;
right: 0;
}
&--left {
right: 100%;
top: 0;
margin-right: 0.5rem;
margin-top: 0;
}
&--right {
left: 100%;
top: 0;
margin-left: 0.5rem;
margin-top: 0;
}
}
&__item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.5rem 0.75rem;
border-radius: 8px;
cursor: pointer;
transition: all 0.2s ease;
color: #27272a;
&:hover:not(&--disabled) {
background: rgba(220, 38, 38, 0.1);
color: #dc2626;
}
&--active:not(&--disabled) {
background: rgba(220, 38, 38, 0.05);
}
&--selected {
color: #dc2626;
font-weight: 600;
}
&--disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
&__item-icon {
width: 1.25rem;
height: 1.25rem;
flex-shrink: 0;
}
&__item-label {
flex: 1;
font-size: 0.875rem;
}
&__item-shortcut {
font-size: 0.75rem;
color: #71717a;
margin-left: auto;
}
&__item-check {
width: 1rem;
height: 1rem;
color: #dc2626;
margin-left: auto;
}
}
// Transition animations
.dropdown-enter-active,
.dropdown-leave-active {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.dropdown-enter-from {
opacity: 0;
transform: translateY(-10px) scale(0.95);
filter: blur(4px);
}
.dropdown-leave-to {
opacity: 0;
transform: translateY(-10px) scale(0.95);
filter: blur(4px);
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -1,230 +0,0 @@
# Glass Morphism Implementation Guide
## Overview
This document details the glass morphism design patterns implemented in the MonacoUSA Portal, following the design philosophy outlined in design-system.md.
## Implementation Date
December 2024
## Latest Update
January 2025 - Enhanced Sidebar Design with Fixed Width
## Key Changes
### 1. Global SCSS Architecture
Created `assets/scss/main.scss` with comprehensive design system implementation:
- Monaco red color spectrum variables
- Glass morphism mixins
- Animation utilities
- Component classes
- Responsive breakpoints
### 2. Layout Transformations
#### Enhanced Sidebar Design (All Layouts)
- **Fixed Width**: 280px permanent sidebar (non-collapsible)
- **Enhanced Glass Effects**: 30px blur with improved opacity
- **No Hamburger Menu**: Removed collapse/expand functionality entirely
- **Vertical Profile Card**: Small avatar stacked above user info
- **Animated Navigation**: Hover effects with smooth transitions
- **Glass Badges**: Consistent styling across all access levels
- **Shimmer Animation**: Logo with subtle shimmer effect
#### Admin Layout (`layouts/admin.vue`)
- **Enhanced Glass Navigation**: Fixed 280px width with enhanced blur
- **Gradient App Bar**: Dark gradient from monaco-red-600 to monaco-red-900
- **Glass Icon Buttons**: Semi-transparent with hover effects
- **Profile Card**: Vertical layout with admin-specific menu options
#### Board Layout (`layouts/board.vue`)
- **Enhanced Glass Navigation**: Consistent 280px fixed width
- **Gradient App Bar**: Softer gradient from monaco-red to brown tones
- **Glass Search Dialog**: Search overlay with glass card styling
- **Profile Card**: Board member profile with vertical layout
#### Member Layout (`layouts/member.vue`)
- **Enhanced Glass Navigation**: Same 280px fixed width design
- **Gradient App Bar**: Lightest gradient for member access level
- **Streamlined Navigation**: Fewer menu items with clear hierarchy
- **Profile Card**: Member profile with consistent vertical layout
## Glass Morphism Patterns
### Core Mixins
```scss
// Enhanced glass effect with stronger blur
@mixin enhanced-glass($opacity: 0.9, $blur: 30px) {
background: linear-gradient(
135deg,
rgba(255, 255, 255, $opacity * 0.95),
rgba(255, 255, 255, $opacity * 0.85),
rgba(255, 255, 255, $opacity * 0.75)
);
backdrop-filter: blur($blur) saturate(180%);
-webkit-backdrop-filter: blur($blur) saturate(180%);
border: 1px solid rgba(255, 255, 255, 0.25);
box-shadow:
0 8px 32px 0 rgba(31, 38, 135, 0.37),
inset 0 1px 2px rgba(255, 255, 255, 0.6),
inset 0 -1px 2px rgba(0, 0, 0, 0.05);
}
```
### Component Classes
#### Glass Cards
- `.glass-card`: Base glass effect card
- `.glass-card--dark`: Dark variant for dark backgrounds
- `.glass-card--colored`: Monaco red tinted glass
#### Navigation Items
- `.glass-nav-item`: Main navigation items with hover effects
- `.glass-nav-item-sub`: Sub-navigation items with reduced opacity
- Active states include gradient backgrounds and left border accent
#### Buttons
- `.glass-icon-btn`: Icon buttons with glass effect
- `.monaco-btn--primary`: Primary buttons with Monaco gradient
- `.monaco-btn--glass`: Glass variant buttons
## Visual Hierarchy
### Access Level Differentiation
1. **Admin**: Darkest gradients (monaco-red-900 range)
2. **Board**: Medium gradients (monaco-red-700 range)
3. **Member**: Lightest gradients (monaco-red-500 range)
### Depth & Layering
- Navigation drawer: Fixed 280px width, 90% opacity, 30px blur
- Profile card: Vertical layout with small avatar
- Dropdowns: 95% opacity, 20px blur
- Cards: 80% opacity, 15px blur
- Buttons: 10-20% opacity, 10px blur
- No collapsible functionality - permanent fixed sidebar
## Animation Patterns
### Float Animation
```scss
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
```
Applied to logo for subtle movement.
### Hover Effects
- Navigation items: translateX(2px) on hover
- Buttons: translateY(-1px) with shadow enhancement
- Cards: translateY(-2px) with increased shadow
## Color Usage
### Primary Monaco Red Spectrum
- `#dc2626` - Primary brand color
- `#b91c1c` - Dark accent
- `#ef4444` - Light accent
- `#991b1b` - Deep red for admin
- `#7f1d1d` - Darkest tone
### Gradients
- **Monaco Gradient**: `linear-gradient(135deg, #dc2626 0%, #b91c1c 100%)`
- **Admin Gradient**: Darker tones for authority
- **Board Gradient**: Balanced mid-tones
- **Member Gradient**: Lighter, welcoming tones
## Responsive Considerations
### Breakpoints
- Mobile: < 640px
- Tablet: 640px - 1024px
- Desktop: > 1024px
### Mobile Optimizations
- Reduced blur effects for performance
- Simplified gradients
- Adjusted spacing and margins
- Auto-closing navigation drawer
## Performance Optimizations
### CSS Optimizations
- Hardware acceleration with `transform: translateZ(0)`
- Will-change property for animated elements
- Reduced motion preferences respected
### Blur Performance
- Limited blur radius to maximum 30px
- Selective application on key elements
- Fallback styles for non-supporting browsers
## Browser Compatibility
### Supported Browsers
- Chrome 90+
- Firefox 88+
- Safari 14+
- Edge 90+
### Fallbacks
- Solid backgrounds for browsers without backdrop-filter
- Standard box-shadows without blur
- Graceful degradation for older browsers
## Implementation Checklist
### Completed
- [x] Global SCSS architecture
- [x] Admin layout glass morphism
- [x] Board layout glass morphism
- [x] Member layout glass morphism
- [x] Monaco color system integration
- [x] Animation patterns
- [x] Responsive design
- [x] Browser compatibility
### Future Enhancements
- [ ] Component library with glass variants
- [ ] Dark mode glass effects
- [ ] Advanced animation sequences
- [ ] Performance monitoring
- [ ] A/B testing for user preference
## Usage Guidelines
### When to Use Glass Effects
1. **Primary UI Elements**: Navigation, headers, cards
2. **Overlays**: Modals, dialogs, dropdowns
3. **Interactive Elements**: Buttons, form fields
4. **Status Indicators**: Badges, chips, alerts
### When to Avoid
1. **Body Text Areas**: Maintain readability
2. **High-Frequency Updates**: Performance consideration
3. **Critical Actions**: Ensure clarity over aesthetics
4. **Accessibility Concerns**: Provide alternatives
## Maintenance Notes
### File Locations
- **SCSS**: `assets/scss/main.scss`
- **Layouts**: `layouts/admin.vue`, `layouts/board.vue`, `layouts/member.vue`
- **Config**: `nuxt.config.ts` (theme configuration)
### Update Process
1. Modify SCSS variables in main.scss
2. Test across all layouts
3. Verify responsive behavior
4. Check browser compatibility
5. Update this documentation
## Related Documentation
- [Design System](./design-system.md)
- [Implementation Guide](./README.md)
- [Component Architecture](../components/README.md)
---
*Last Updated: December 2024*
*Implementation Version: 1.0.0*

View File

@ -1,651 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MonacoUSA Portal - Board Dashboard</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/lucide@latest"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap');
body {
font-family: 'Inter', sans-serif;
background: linear-gradient(135deg, #fff5f5 0%, #ffffff 25%, #fef2f2 50%, #ffffff 75%, #fff5f5 100%);
}
/* Monaco Gradients */
.monaco-gradient {
background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%);
}
.monaco-gradient-dark {
background: linear-gradient(135deg, #991b1b 0%, #7f1d1d 100%);
}
.monaco-gradient-vibrant {
background: linear-gradient(135deg, #ef4444 0%, #dc2626 50%, #991b1b 100%);
}
/* Glass Effects */
.glass {
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.4);
box-shadow: 0 8px 32px 0 rgba(220, 38, 38, 0.08);
}
.glass-red {
background: linear-gradient(135deg, rgba(254, 202, 202, 0.3) 0%, rgba(252, 165, 165, 0.2) 100%);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(220, 38, 38, 0.15);
box-shadow: 0 8px 32px 0 rgba(220, 38, 38, 0.12);
}
.glass-dark {
background: rgba(0, 0, 0, 0.03);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
/* Animated Elements */
@keyframes slideInRight {
from { transform: translateX(100px); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
@keyframes slideInUp {
from { transform: translateY(30px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
.slide-in-right {
animation: slideInRight 0.8s ease-out;
}
.slide-in-up {
animation: slideInUp 0.6s ease-out;
}
/* Status Indicators */
@keyframes pulse-green {
0%, 100% { box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.7); }
70% { box-shadow: 0 0 0 10px rgba(34, 197, 94, 0); }
}
@keyframes pulse-red {
0%, 100% { box-shadow: 0 0 0 0 rgba(220, 38, 38, 0.7); }
70% { box-shadow: 0 0 0 10px rgba(220, 38, 38, 0); }
}
@keyframes pulse-amber {
0%, 100% { box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.7); }
70% { box-shadow: 0 0 0 10px rgba(245, 158, 11, 0); }
}
.pulse-green { animation: pulse-green 2s infinite; }
.pulse-red { animation: pulse-red 2s infinite; }
.pulse-amber { animation: pulse-amber 2s infinite; }
/* Card Effects */
.card-3d {
transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
transform-style: preserve-3d;
}
.card-3d:hover {
transform: translateY(-10px) rotateX(5deg) scale(1.02);
box-shadow: 0 30px 60px rgba(220, 38, 38, 0.2);
}
/* Gradient Text */
.gradient-text {
background: linear-gradient(135deg, #dc2626 0%, #7f1d1d 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
/* Floating Elements */
@keyframes float {
0%, 100% { transform: translateY(0px) rotate(0deg); }
50% { transform: translateY(-20px) rotate(2deg); }
}
.floating {
animation: float 6s ease-in-out infinite;
}
/* Shimmer Effect */
@keyframes shimmer {
0% { background-position: -1000px 0; }
100% { background-position: 1000px 0; }
}
.shimmer {
background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.4) 50%, transparent 100%);
background-size: 1000px 100%;
animation: shimmer 3s infinite;
}
/* Data Grid Styles */
.data-row {
transition: all 0.3s ease;
}
.data-row:hover {
background: linear-gradient(90deg, rgba(220, 38, 38, 0.05) 0%, rgba(220, 38, 38, 0.02) 100%);
transform: translateX(5px);
}
/* Chart Container */
.chart-container {
position: relative;
height: 300px;
}
</style>
</head>
<body class="min-h-screen overflow-x-hidden">
<!-- Animated Background -->
<div class="fixed inset-0 overflow-hidden pointer-events-none">
<div class="absolute -top-40 -right-40 w-80 h-80 bg-red-200 rounded-full opacity-20 blur-3xl floating"></div>
<div class="absolute -bottom-40 -left-40 w-80 h-80 bg-red-100 rounded-full opacity-20 blur-3xl floating" style="animation-delay: 2s;"></div>
<div class="absolute top-1/2 left-1/3 w-60 h-60 bg-red-300 rounded-full opacity-10 blur-3xl floating" style="animation-delay: 4s;"></div>
</div>
<!-- Executive Header -->
<header class="fixed top-0 left-0 right-0 z-50 glass border-b border-red-100">
<div class="px-6 py-4">
<div class="flex items-center justify-between">
<!-- Brand -->
<div class="flex items-center space-x-4">
<div class="relative group">
<div class="absolute inset-0 monaco-gradient rounded-xl blur opacity-50 group-hover:opacity-75 transition-opacity"></div>
<div class="relative w-12 h-12 bg-white rounded-xl overflow-hidden shadow-xl">
<div class="h-1/2 bg-red-600"></div>
<div class="h-1/2 bg-white"></div>
</div>
</div>
<div>
<h1 class="text-2xl font-bold gradient-text">MonacoUSA Portal</h1>
<p class="text-xs text-gray-600 font-medium">Board Executive Dashboard</p>
</div>
</div>
<!-- Center Controls -->
<div class="hidden lg:flex items-center space-x-4">
<div class="flex items-center space-x-2 px-4 py-2 glass-red rounded-full">
<div class="w-2 h-2 bg-green-500 rounded-full pulse-green"></div>
<span class="text-sm font-medium text-gray-700">System Status: Operational</span>
</div>
<div class="flex items-center space-x-2 px-4 py-2 glass rounded-full">
<i data-lucide="users" class="w-4 h-4 text-gray-500"></i>
<span class="text-sm font-medium text-gray-700">287 Active Members</span>
</div>
</div>
<!-- Actions -->
<div class="flex items-center space-x-3">
<button class="px-4 py-2 monaco-gradient text-white rounded-lg font-semibold hover:shadow-lg transition-all flex items-center space-x-2">
<i data-lucide="user-plus" class="w-4 h-4"></i>
<span>Add Member</span>
</button>
<button class="px-4 py-2 glass hover:bg-red-50 rounded-lg font-semibold transition-all flex items-center space-x-2">
<i data-lucide="download" class="w-4 h-4"></i>
<span>Export</span>
</button>
<!-- Profile -->
<div class="flex items-center space-x-3 pl-3 ml-3 border-l border-gray-200">
<div class="text-right">
<p class="text-sm font-semibold text-gray-900">Board Executive</p>
<p class="text-xs text-red-600">Administrative Access</p>
</div>
<div class="relative">
<img src="https://ui-avatars.com/api/?name=Board+Executive&background=dc2626&color=fff&bold=true"
alt="Profile" class="w-11 h-11 rounded-full ring-2 ring-red-200">
<span class="absolute bottom-0 right-0 w-3 h-3 bg-green-500 border-2 border-white rounded-full"></span>
</div>
</div>
</div>
</div>
</div>
</header>
<!-- Executive Sidebar -->
<aside class="fixed left-0 top-20 bottom-0 w-72 glass-dark border-r border-red-100/20 overflow-y-auto">
<!-- Quick Stats Panel -->
<div class="p-6 border-b border-red-100/20">
<div class="glass-red rounded-2xl p-4">
<h3 class="text-sm font-semibold text-gray-700 mb-3">Today's Overview</h3>
<div class="grid grid-cols-2 gap-3">
<div class="text-center">
<p class="text-2xl font-bold gradient-text">43</p>
<p class="text-xs text-gray-600">Overdue</p>
</div>
<div class="text-center">
<p class="text-2xl font-bold text-green-600">$43K</p>
<p class="text-xs text-gray-600">Revenue</p>
</div>
<div class="text-center">
<p class="text-2xl font-bold text-blue-600">89%</p>
<p class="text-xs text-gray-600">Attendance</p>
</div>
<div class="text-center">
<p class="text-2xl font-bold text-purple-600">15</p>
<p class="text-xs text-gray-600">New</p>
</div>
</div>
</div>
</div>
<!-- Navigation -->
<nav class="p-4">
<div class="space-y-1">
<a href="#" class="flex items-center space-x-3 px-4 py-3 rounded-xl monaco-gradient text-white font-medium shadow-lg">
<i data-lucide="shield" class="w-5 h-5"></i>
<span>Executive Overview</span>
</a>
<a href="#" class="flex items-center space-x-3 px-4 py-3 rounded-xl text-gray-700 hover:bg-red-50 hover:text-red-600 font-medium transition-all">
<i data-lucide="users" class="w-5 h-5"></i>
<span>Member Management</span>
<span class="ml-auto text-xs bg-red-100 text-red-600 px-2 py-1 rounded-full">43</span>
</a>
<a href="#" class="flex items-center space-x-3 px-4 py-3 rounded-xl text-gray-700 hover:bg-red-50 hover:text-red-600 font-medium transition-all">
<i data-lucide="credit-card" class="w-5 h-5"></i>
<span>Dues & Revenue</span>
</a>
<a href="#" class="flex items-center space-x-3 px-4 py-3 rounded-xl text-gray-700 hover:bg-red-50 hover:text-red-600 font-medium transition-all">
<i data-lucide="calendar" class="w-5 h-5"></i>
<span>Event Planning</span>
</a>
<a href="#" class="flex items-center space-x-3 px-4 py-3 rounded-xl text-gray-700 hover:bg-red-50 hover:text-red-600 font-medium transition-all">
<i data-lucide="bar-chart-3" class="w-5 h-5"></i>
<span>Analytics</span>
</a>
<a href="#" class="flex items-center space-x-3 px-4 py-3 rounded-xl text-gray-700 hover:bg-red-50 hover:text-red-600 font-medium transition-all">
<i data-lucide="mail" class="w-5 h-5"></i>
<span>Communications</span>
<span class="ml-auto w-2 h-2 bg-red-500 rounded-full pulse-red"></span>
</a>
<a href="#" class="flex items-center space-x-3 px-4 py-3 rounded-xl text-gray-700 hover:bg-red-50 hover:text-red-600 font-medium transition-all">
<i data-lucide="settings" class="w-5 h-5"></i>
<span>Settings</span>
</a>
</div>
</nav>
<!-- Board Access Badge -->
<div class="absolute bottom-4 left-4 right-4">
<div class="monaco-gradient rounded-xl p-4 text-white text-center">
<i data-lucide="shield-check" class="w-8 h-8 mx-auto mb-2"></i>
<p class="text-xs font-semibold">BOARD EXECUTIVE</p>
<p class="text-xs opacity-80">Full Administrative Access</p>
</div>
</div>
</aside>
<!-- Main Content -->
<main class="ml-72 mt-20 p-8">
<!-- Executive Header -->
<div class="mb-8 slide-in-right">
<div class="flex items-center justify-between">
<div>
<h2 class="text-4xl font-bold gradient-text mb-2">Board Executive Dashboard</h2>
<p class="text-gray-600">Real-time organization management and insights</p>
</div>
<div class="flex items-center space-x-4">
<div class="text-right">
<p class="text-sm text-gray-500">Last Updated</p>
<p class="text-lg font-semibold text-gray-900">2 min ago</p>
</div>
<button class="p-3 glass rounded-xl hover:bg-red-50 transition-colors">
<i data-lucide="refresh-cw" class="w-5 h-5 text-gray-700"></i>
</button>
</div>
</div>
</div>
<!-- KPI Cards -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<!-- Total Members KPI -->
<div class="glass rounded-2xl p-6 card-3d slide-in-up">
<div class="flex items-center justify-between mb-4">
<div class="w-14 h-14 monaco-gradient rounded-xl flex items-center justify-center shadow-lg">
<i data-lucide="users" class="w-7 h-7 text-white"></i>
</div>
<div class="flex items-center space-x-1 text-green-600">
<i data-lucide="trending-up" class="w-4 h-4"></i>
<span class="text-sm font-bold">+12%</span>
</div>
</div>
<p class="text-sm text-gray-500 mb-1">Total Members</p>
<p class="text-3xl font-bold text-gray-900">287</p>
<div class="mt-4 flex items-center justify-between text-xs">
<span class="text-gray-500">Target: 300</span>
<div class="w-24 bg-gray-200 rounded-full h-2">
<div class="monaco-gradient h-2 rounded-full" style="width: 95.7%"></div>
</div>
</div>
</div>
<!-- Revenue KPI -->
<div class="glass rounded-2xl p-6 card-3d slide-in-up" style="animation-delay: 0.1s">
<div class="flex items-center justify-between mb-4">
<div class="w-14 h-14 bg-gradient-to-br from-green-500 to-emerald-600 rounded-xl flex items-center justify-center shadow-lg">
<i data-lucide="dollar-sign" class="w-7 h-7 text-white"></i>
</div>
<div class="flex items-center space-x-1 text-green-600">
<i data-lucide="trending-up" class="w-4 h-4"></i>
<span class="text-sm font-bold">+8%</span>
</div>
</div>
<p class="text-sm text-gray-500 mb-1">YTD Revenue</p>
<p class="text-3xl font-bold text-gray-900">$43,050</p>
<div class="mt-4 flex items-center justify-between text-xs">
<span class="text-gray-500">Goal: $50,000</span>
<div class="w-24 bg-gray-200 rounded-full h-2">
<div class="bg-gradient-to-r from-green-500 to-emerald-600 h-2 rounded-full" style="width: 86%"></div>
</div>
</div>
</div>
<!-- Overdue Dues KPI -->
<div class="glass rounded-2xl p-6 card-3d slide-in-up" style="animation-delay: 0.2s">
<div class="flex items-center justify-between mb-4">
<div class="w-14 h-14 bg-gradient-to-br from-amber-500 to-orange-600 rounded-xl flex items-center justify-center shadow-lg">
<i data-lucide="alert-triangle" class="w-7 h-7 text-white"></i>
</div>
<span class="px-2 py-1 bg-amber-100 text-amber-700 text-xs font-bold rounded-full pulse-amber">ACTION</span>
</div>
<p class="text-sm text-gray-500 mb-1">Overdue Dues</p>
<p class="text-3xl font-bold text-gray-900">43</p>
<div class="mt-4 flex items-center justify-between text-xs">
<span class="text-gray-500">Amount: $6,450</span>
<button class="text-amber-600 font-semibold hover:text-amber-700">View All →</button>
</div>
</div>
<!-- Event Success KPI -->
<div class="glass rounded-2xl p-6 card-3d slide-in-up" style="animation-delay: 0.3s">
<div class="flex items-center justify-between mb-4">
<div class="w-14 h-14 bg-gradient-to-br from-purple-500 to-indigo-600 rounded-xl flex items-center justify-center shadow-lg">
<i data-lucide="calendar-check" class="w-7 h-7 text-white"></i>
</div>
<div class="flex items-center space-x-1 text-green-600">
<i data-lucide="trending-up" class="w-4 h-4"></i>
<span class="text-sm font-bold">+25%</span>
</div>
</div>
<p class="text-sm text-gray-500 mb-1">Avg Attendance</p>
<p class="text-3xl font-bold text-gray-900">89%</p>
<div class="mt-4 flex items-center justify-between text-xs">
<span class="text-gray-500">Last 3 events</span>
<div class="flex -space-x-2">
<div class="w-6 h-6 bg-green-500 rounded-full border-2 border-white"></div>
<div class="w-6 h-6 bg-green-500 rounded-full border-2 border-white"></div>
<div class="w-6 h-6 bg-amber-500 rounded-full border-2 border-white"></div>
</div>
</div>
</div>
</div>
<!-- Priority Actions Banner -->
<div class="mb-8 slide-in-right">
<div class="monaco-gradient-vibrant rounded-2xl p-6 text-white relative overflow-hidden">
<div class="absolute top-0 right-0 w-64 h-64 bg-white rounded-full blur-3xl opacity-10"></div>
<div class="relative z-10 flex items-center justify-between">
<div class="flex items-center space-x-6">
<div class="w-16 h-16 bg-white/20 backdrop-blur rounded-xl flex items-center justify-center">
<i data-lucide="alert-circle" class="w-8 h-8"></i>
</div>
<div>
<h3 class="text-xl font-bold mb-1">43 Members with Overdue Dues</h3>
<p class="text-white/90">Total outstanding amount: $6,450 • Average days overdue: 67</p>
</div>
</div>
<div class="flex space-x-3">
<button class="px-5 py-2.5 bg-white/20 backdrop-blur rounded-lg font-semibold hover:bg-white/30 transition-all">
View Details
</button>
<button class="px-5 py-2.5 bg-white text-red-600 rounded-lg font-semibold hover:shadow-xl transition-all flex items-center space-x-2">
<i data-lucide="send" class="w-4 h-4"></i>
<span>Send Reminders</span>
</button>
</div>
</div>
</div>
</div>
<!-- Main Content Grid -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
<!-- Member Management Panel -->
<div class="lg:col-span-2">
<div class="glass rounded-2xl p-6">
<div class="flex items-center justify-between mb-6">
<h3 class="text-xl font-bold text-gray-900">Dues Management</h3>
<div class="flex space-x-2">
<button class="px-3 py-1.5 text-sm font-medium text-white monaco-gradient rounded-lg">Overdue (43)</button>
<button class="px-3 py-1.5 text-sm font-medium text-gray-600 hover:bg-gray-100 rounded-lg">Due Soon (5)</button>
<button class="px-3 py-1.5 text-sm font-medium text-gray-600 hover:bg-gray-100 rounded-lg">Paid (239)</button>
</div>
</div>
<!-- Member Cards -->
<div class="space-y-4">
<!-- Member Card 1 -->
<div class="glass-red rounded-xl p-4 data-row">
<div class="flex items-center justify-between">
<div class="flex items-center space-x-4">
<img src="https://ui-avatars.com/api/?name=Annette+Anderson&background=dc2626&color=fff"
alt="Member" class="w-12 h-12 rounded-full ring-2 ring-red-200">
<div>
<h4 class="font-semibold text-gray-900">Annette Anderson</h4>
<div class="flex items-center space-x-3 text-sm text-gray-500">
<span>ID: MUSA-16</span>
<span></span>
<span class="text-red-600 font-medium">85 days overdue</span>
</div>
</div>
</div>
<div class="flex items-center space-x-4">
<div class="text-right">
<p class="text-lg font-bold text-gray-900">$150</p>
<p class="text-xs text-gray-500">Due Jun 6, 2024</p>
</div>
<div class="flex space-x-2">
<button class="p-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors">
<i data-lucide="check" class="w-4 h-4"></i>
</button>
<button class="p-2 glass hover:bg-gray-50 rounded-lg transition-colors">
<i data-lucide="mail" class="w-4 h-4"></i>
</button>
<button class="p-2 glass hover:bg-gray-50 rounded-lg transition-colors">
<i data-lucide="more-vertical" class="w-4 h-4"></i>
</button>
</div>
</div>
</div>
</div>
<!-- Member Card 2 -->
<div class="glass-red rounded-xl p-4 data-row">
<div class="flex items-center justify-between">
<div class="flex items-center space-x-4">
<img src="https://ui-avatars.com/api/?name=Danilo+Copiz&background=dc2626&color=fff"
alt="Member" class="w-12 h-12 rounded-full ring-2 ring-red-200">
<div>
<h4 class="font-semibold text-gray-900">Danilo Copiz</h4>
<div class="flex items-center space-x-3 text-sm text-gray-500">
<span>ID: MUSA-19</span>
<span></span>
<span class="text-red-600 font-medium">85 days overdue</span>
</div>
</div>
</div>
<div class="flex items-center space-x-4">
<div class="text-right">
<p class="text-lg font-bold text-gray-900">$150</p>
<p class="text-xs text-gray-500">Due Jun 6, 2024</p>
</div>
<div class="flex space-x-2">
<button class="p-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors">
<i data-lucide="check" class="w-4 h-4"></i>
</button>
<button class="p-2 glass hover:bg-gray-50 rounded-lg transition-colors">
<i data-lucide="mail" class="w-4 h-4"></i>
</button>
<button class="p-2 glass hover:bg-gray-50 rounded-lg transition-colors">
<i data-lucide="more-vertical" class="w-4 h-4"></i>
</button>
</div>
</div>
</div>
</div>
<!-- Member Card 3 -->
<div class="glass-red rounded-xl p-4 data-row">
<div class="flex items-center justify-between">
<div class="flex items-center space-x-4">
<img src="https://ui-avatars.com/api/?name=Ian+Sosso&background=dc2626&color=fff"
alt="Member" class="w-12 h-12 rounded-full ring-2 ring-red-200">
<div>
<h4 class="font-semibold text-gray-900">Ian Sosso</h4>
<div class="flex items-center space-x-3 text-sm text-gray-500">
<span>ID: MUSA-78</span>
<span></span>
<span class="text-red-600 font-medium">81 days overdue</span>
</div>
</div>
</div>
<div class="flex items-center space-x-4">
<div class="text-right">
<p class="text-lg font-bold text-gray-900">$150</p>
<p class="text-xs text-gray-500">Due Jun 10, 2024</p>
</div>
<div class="flex space-x-2">
<button class="p-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors">
<i data-lucide="check" class="w-4 h-4"></i>
</button>
<button class="p-2 glass hover:bg-gray-50 rounded-lg transition-colors">
<i data-lucide="mail" class="w-4 h-4"></i>
</button>
<button class="p-2 glass hover:bg-gray-50 rounded-lg transition-colors">
<i data-lucide="more-vertical" class="w-4 h-4"></i>
</button>
</div>
</div>
</div>
</div>
</div>
<div class="mt-6 text-center">
<button class="text-red-600 hover:text-red-700 font-semibold">
View All 43 Overdue Members →
</button>
</div>
</div>
</div>
<!-- Analytics Panel -->
<div class="space-y-6">
<!-- Revenue Chart -->
<div class="glass rounded-2xl p-6">
<h3 class="text-lg font-bold text-gray-900 mb-4">Revenue Trend</h3>
<div class="chart-container">
<canvas id="revenueChart"></canvas>
</div>
</div>
<!-- Quick Actions -->
<div class="glass rounded-2xl p-6">
<h3 class="text-lg font-bold text-gray-900 mb-4">Quick Actions</h3>
<div class="space-y-3">
<button class="w-full flex items-center justify-between p-3 glass-red rounded-xl hover:shadow-md transition-all group">
<div class="flex items-center space-x-3">
<i data-lucide="user-plus" class="w-5 h-5 text-red-600"></i>
<span class="font-medium text-gray-700">Add New Member</span>
</div>
<i data-lucide="arrow-right" class="w-4 h-4 text-gray-400 group-hover:translate-x-1 transition-transform"></i>
</button>
<button class="w-full flex items-center justify-between p-3 glass-red rounded-xl hover:shadow-md transition-all group">
<div class="flex items-center space-x-3">
<i data-lucide="calendar-plus" class="w-5 h-5 text-red-600"></i>
<span class="font-medium text-gray-700">Create Event</span>
</div>
<i data-lucide="arrow-right" class="w-4 h-4 text-gray-400 group-hover:translate-x-1 transition-transform"></i>
</button>
<button class="w-full flex items-center justify-between p-3 glass-red rounded-xl hover:shadow-md transition-all group">
<div class="flex items-center space-x-3">
<i data-lucide="file-text" class="w-5 h-5 text-red-600"></i>
<span class="font-medium text-gray-700">Generate Report</span>
</div>
<i data-lucide="arrow-right" class="w-4 h-4 text-gray-400 group-hover:translate-x-1 transition-transform"></i>
</button>
</div>
</div>
</div>
</div>
</main>
<script>
lucide.createIcons();
// Revenue Chart
const ctx = document.getElementById('revenueChart').getContext('2d');
const gradient = ctx.createLinearGradient(0, 0, 0, 250);
gradient.addColorStop(0, 'rgba(220, 38, 38, 0.3)');
gradient.addColorStop(1, 'rgba(220, 38, 38, 0)');
new Chart(ctx, {
type: 'line',
data: {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
datasets: [{
label: 'Revenue',
data: [32000, 34000, 35500, 38000, 41000, 43050],
borderColor: '#dc2626',
backgroundColor: gradient,
tension: 0.4,
fill: true,
pointBackgroundColor: '#fff',
pointBorderColor: '#dc2626',
pointBorderWidth: 2,
pointRadius: 4,
pointHoverRadius: 6
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
}
},
scales: {
y: {
beginAtZero: false,
grid: {
display: false
},
ticks: {
callback: function(value) {
return '$' + (value / 1000) + 'k';
}
}
},
x: {
grid: {
display: false
}
}
}
}
});
</script>
</body>
</html>

View File

@ -1,601 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MonacoUSA Portal - Member Dashboard</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/lucide@latest"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap');
body {
font-family: 'Inter', sans-serif;
background: linear-gradient(135deg, #fef3f3 0%, #ffffff 50%, #fef3f3 100%);
}
/* Monaco Red Gradient Variations */
.monaco-gradient {
background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%);
}
.monaco-gradient-soft {
background: linear-gradient(135deg, #fecaca 0%, #fca5a5 100%);
}
.monaco-gradient-vibrant {
background: linear-gradient(135deg, #ef4444 0%, #dc2626 50%, #991b1b 100%);
}
.monaco-gradient-subtle {
background: linear-gradient(135deg, #fee2e2 0%, #fecaca 100%);
}
/* Glass Morphism Effects */
.glass {
background: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.3);
box-shadow: 0 8px 32px 0 rgba(220, 38, 38, 0.1);
}
.glass-red {
background: rgba(220, 38, 38, 0.1);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(220, 38, 38, 0.2);
box-shadow: 0 8px 32px 0 rgba(220, 38, 38, 0.15);
}
.glass-dark {
background: rgba(0, 0, 0, 0.05);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
/* Animated Gradients */
.animated-gradient {
background: linear-gradient(270deg, #dc2626, #ef4444, #dc2626);
background-size: 400% 400%;
animation: gradientShift 15s ease infinite;
}
@keyframes gradientShift {
0%, 100% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
}
/* Glow Effects */
.glow-red {
box-shadow: 0 0 40px rgba(220, 38, 38, 0.3);
}
.glow-red-intense {
box-shadow: 0 0 60px rgba(220, 38, 38, 0.5),
0 0 100px rgba(220, 38, 38, 0.3);
}
/* Card Hover Effects */
.card-hover {
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.card-hover:hover {
transform: translateY(-8px) scale(1.02);
box-shadow: 0 20px 60px rgba(220, 38, 38, 0.2);
}
/* Floating Animation */
@keyframes float {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(-10px); }
}
.floating {
animation: float 6s ease-in-out infinite;
}
/* Pulse Animation */
@keyframes pulse-red {
0%, 100% {
box-shadow: 0 0 0 0 rgba(220, 38, 38, 0.7);
}
70% {
box-shadow: 0 0 0 20px rgba(220, 38, 38, 0);
}
}
.pulse {
animation: pulse-red 2s infinite;
}
/* Background Pattern */
.pattern-monaco {
background-color: #ffffff;
background-image:
radial-gradient(circle at 20% 80%, rgba(220, 38, 38, 0.1) 0%, transparent 50%),
radial-gradient(circle at 80% 20%, rgba(220, 38, 38, 0.08) 0%, transparent 50%),
radial-gradient(circle at 40% 40%, rgba(220, 38, 38, 0.05) 0%, transparent 50%);
}
/* Sidebar Styles */
.sidebar-item {
position: relative;
transition: all 0.3s ease;
overflow: hidden;
}
.sidebar-item::before {
content: '';
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 0;
background: linear-gradient(90deg, rgba(220, 38, 38, 0.1) 0%, transparent 100%);
transition: width 0.3s ease;
}
.sidebar-item:hover::before {
width: 100%;
}
.sidebar-item.active {
background: linear-gradient(90deg, rgba(220, 38, 38, 0.15) 0%, rgba(220, 38, 38, 0.05) 100%);
border-left: 4px solid #dc2626;
}
/* Number Counter Animation */
@keyframes countUp {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.count-up {
animation: countUp 0.8s ease-out;
}
/* Shimmer Effect */
@keyframes shimmer {
0% { background-position: -1000px 0; }
100% { background-position: 1000px 0; }
}
.shimmer {
background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.3) 50%, transparent 100%);
background-size: 1000px 100%;
animation: shimmer 3s infinite;
}
</style>
</head>
<body class="pattern-monaco min-h-screen">
<!-- Animated Background Elements -->
<div class="fixed inset-0 overflow-hidden pointer-events-none">
<div class="absolute top-20 left-20 w-72 h-72 bg-red-200 rounded-full opacity-20 blur-3xl floating"></div>
<div class="absolute bottom-20 right-20 w-96 h-96 bg-red-300 rounded-full opacity-15 blur-3xl floating" style="animation-delay: 3s;"></div>
<div class="absolute top-1/2 left-1/2 w-64 h-64 bg-red-100 rounded-full opacity-20 blur-3xl floating" style="animation-delay: 1.5s;"></div>
</div>
<!-- Premium Header -->
<header class="fixed top-0 left-0 right-0 z-50 glass border-b border-white/20">
<div class="px-6 py-4">
<div class="flex items-center justify-between">
<!-- Left Section -->
<div class="flex items-center space-x-4">
<!-- Monaco Flag Logo Animation -->
<div class="relative group">
<div class="absolute inset-0 monaco-gradient rounded-xl blur-lg opacity-50 group-hover:opacity-75 transition-opacity"></div>
<div class="relative w-12 h-12 bg-white rounded-xl overflow-hidden shadow-xl">
<div class="h-1/2 bg-red-600"></div>
<div class="h-1/2 bg-white"></div>
</div>
</div>
<div>
<h1 class="text-2xl font-bold bg-gradient-to-r from-red-600 to-red-800 bg-clip-text text-transparent">
MonacoUSA Portal
</h1>
<p class="text-xs text-gray-600 font-medium">Excellence in Community</p>
</div>
</div>
<!-- Center Search -->
<div class="hidden lg:flex items-center">
<div class="relative">
<input type="text" placeholder="Search members, events, documents..."
class="w-96 px-5 py-2.5 pl-12 glass rounded-full text-sm focus:outline-none focus:ring-2 focus:ring-red-500/30 transition-all">
<i data-lucide="search" class="absolute left-4 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400"></i>
<kbd class="absolute right-4 top-1/2 -translate-y-1/2 text-xs bg-gray-100 px-2 py-1 rounded">⌘K</kbd>
</div>
</div>
<!-- Right Section -->
<div class="flex items-center space-x-3">
<!-- Quick Actions -->
<button class="p-2.5 glass rounded-xl hover:bg-red-50 transition-colors group">
<i data-lucide="plus" class="w-5 h-5 text-gray-700 group-hover:text-red-600 transition-colors"></i>
</button>
<!-- Notifications with Badge -->
<button class="relative p-2.5 glass rounded-xl hover:bg-red-50 transition-colors group">
<i data-lucide="bell" class="w-5 h-5 text-gray-700 group-hover:text-red-600 transition-colors"></i>
<span class="absolute top-1 right-1 w-2.5 h-2.5 bg-red-500 rounded-full pulse"></span>
</button>
<!-- Premium User Profile -->
<div class="flex items-center space-x-3 pl-3 ml-3 border-l border-gray-200">
<div class="text-right">
<p class="text-sm font-semibold text-gray-900">Matthew Ciaccio</p>
<p class="text-xs text-gray-500">Gold Member</p>
</div>
<div class="relative group cursor-pointer">
<div class="absolute inset-0 monaco-gradient rounded-full blur opacity-0 group-hover:opacity-50 transition-opacity"></div>
<img src="https://ui-avatars.com/api/?name=Matthew+Ciaccio&background=dc2626&color=fff&bold=true"
alt="Profile" class="relative w-11 h-11 rounded-full ring-2 ring-red-100">
</div>
</div>
</div>
</div>
</div>
</header>
<!-- Modern Sidebar -->
<aside class="fixed left-0 top-20 bottom-0 w-72 glass-dark border-r border-white/10 overflow-y-auto">
<!-- User Status Card -->
<div class="p-6">
<div class="glass-red rounded-2xl p-4 relative overflow-hidden">
<div class="absolute top-0 right-0 w-32 h-32 monaco-gradient rounded-full blur-2xl opacity-30"></div>
<div class="relative">
<div class="flex items-center space-x-3 mb-3">
<div class="w-12 h-12 rounded-xl monaco-gradient flex items-center justify-center">
<i data-lucide="crown" class="w-6 h-6 text-white"></i>
</div>
<div>
<p class="text-sm font-semibold text-gray-900">Gold Member</p>
<p class="text-xs text-gray-600">Since 2019</p>
</div>
</div>
<div class="space-y-2">
<div class="flex justify-between text-xs">
<span class="text-gray-600">Status</span>
<span class="font-semibold text-green-600">Active</span>
</div>
<div class="flex justify-between text-xs">
<span class="text-gray-600">Points</span>
<span class="font-semibold text-gray-900">2,450</span>
</div>
</div>
</div>
</div>
</div>
<!-- Navigation -->
<nav class="px-4 pb-6">
<div class="space-y-1">
<a href="#" class="sidebar-item active flex items-center space-x-3 px-4 py-3 rounded-xl text-red-600 font-medium">
<i data-lucide="layout-dashboard" class="w-5 h-5"></i>
<span>Dashboard</span>
<span class="ml-auto text-xs bg-red-100 text-red-600 px-2 py-1 rounded-full">New</span>
</a>
<a href="#" class="sidebar-item flex items-center space-x-3 px-4 py-3 rounded-xl text-gray-700 hover:text-red-600 font-medium">
<i data-lucide="calendar-days" class="w-5 h-5"></i>
<span>Events</span>
<span class="ml-auto text-xs text-gray-400">12</span>
</a>
<a href="#" class="sidebar-item flex items-center space-x-3 px-4 py-3 rounded-xl text-gray-700 hover:text-red-600 font-medium">
<i data-lucide="users" class="w-5 h-5"></i>
<span>Members</span>
</a>
<a href="#" class="sidebar-item flex items-center space-x-3 px-4 py-3 rounded-xl text-gray-700 hover:text-red-600 font-medium">
<i data-lucide="wallet" class="w-5 h-5"></i>
<span>Dues & Payments</span>
</a>
<a href="#" class="sidebar-item flex items-center space-x-3 px-4 py-3 rounded-xl text-gray-700 hover:text-red-600 font-medium">
<i data-lucide="file-text" class="w-5 h-5"></i>
<span>Documents</span>
</a>
<a href="#" class="sidebar-item flex items-center space-x-3 px-4 py-3 rounded-xl text-gray-700 hover:text-red-600 font-medium">
<i data-lucide="message-circle" class="w-5 h-5"></i>
<span>Messages</span>
<span class="ml-auto w-2 h-2 bg-red-500 rounded-full pulse"></span>
</a>
</div>
<div class="mt-6 pt-6 border-t border-gray-200">
<p class="px-4 text-xs font-semibold text-gray-400 uppercase tracking-wider mb-3">Quick Actions</p>
<div class="space-y-1">
<button class="w-full sidebar-item flex items-center space-x-3 px-4 py-3 rounded-xl text-gray-700 hover:text-red-600 font-medium">
<i data-lucide="download" class="w-5 h-5"></i>
<span>Download ID Card</span>
</button>
<button class="w-full sidebar-item flex items-center space-x-3 px-4 py-3 rounded-xl text-gray-700 hover:text-red-600 font-medium">
<i data-lucide="headphones" class="w-5 h-5"></i>
<span>Support</span>
</button>
</div>
</div>
</nav>
</aside>
<!-- Main Content Area -->
<main class="ml-72 mt-20 p-8">
<!-- Hero Welcome Section -->
<div class="mb-8">
<div class="glass rounded-3xl p-8 relative overflow-hidden">
<div class="absolute top-0 right-0 w-96 h-96 monaco-gradient rounded-full blur-3xl opacity-10"></div>
<div class="relative z-10">
<h2 class="text-4xl font-bold text-gray-900 mb-2">Welcome back, Matthew! 👋</h2>
<p class="text-gray-600 text-lg">Your membership is active and you have 2 upcoming events this week.</p>
<!-- Quick Stats Bar -->
<div class="flex items-center space-x-8 mt-6">
<div class="flex items-center space-x-2">
<div class="w-2 h-2 bg-green-500 rounded-full pulse"></div>
<span class="text-sm text-gray-600">Portal Status: <strong class="text-green-600">Online</strong></span>
</div>
<div class="flex items-center space-x-2">
<i data-lucide="users" class="w-4 h-4 text-gray-400"></i>
<span class="text-sm text-gray-600">Members Online: <strong>34</strong></span>
</div>
<div class="flex items-center space-x-2">
<i data-lucide="calendar" class="w-4 h-4 text-gray-400"></i>
<span class="text-sm text-gray-600">Next Event: <strong>Dec 15</strong></span>
</div>
</div>
</div>
</div>
</div>
<!-- Premium Stats Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<!-- Membership Card -->
<div class="glass rounded-2xl p-6 card-hover relative overflow-hidden group">
<div class="absolute inset-0 monaco-gradient opacity-0 group-hover:opacity-10 transition-opacity"></div>
<div class="relative">
<div class="flex items-center justify-between mb-4">
<div class="w-12 h-12 rounded-xl bg-gradient-to-br from-green-400 to-green-600 flex items-center justify-center shadow-lg">
<i data-lucide="check-circle" class="w-6 h-6 text-white"></i>
</div>
<span class="text-xs font-bold text-green-600 bg-green-100 px-3 py-1 rounded-full">ACTIVE</span>
</div>
<p class="text-sm text-gray-500 mb-1">Membership Status</p>
<p class="text-3xl font-bold text-gray-900 count-up">Active</p>
<div class="mt-4 pt-4 border-t border-gray-100">
<div class="flex items-center justify-between text-xs">
<span class="text-gray-500">Expires</span>
<span class="font-semibold text-gray-700">Dec 31, 2024</span>
</div>
</div>
</div>
</div>
<!-- Dues Card -->
<div class="glass rounded-2xl p-6 card-hover relative overflow-hidden group">
<div class="absolute inset-0 animated-gradient opacity-0 group-hover:opacity-10 transition-opacity"></div>
<div class="relative">
<div class="flex items-center justify-between mb-4">
<div class="w-12 h-12 rounded-xl monaco-gradient flex items-center justify-center shadow-lg">
<i data-lucide="credit-card" class="w-6 h-6 text-white"></i>
</div>
<span class="text-xs font-bold text-amber-600 bg-amber-100 px-3 py-1 rounded-full shimmer">DUE SOON</span>
</div>
<p class="text-sm text-gray-500 mb-1">Annual Dues</p>
<p class="text-3xl font-bold text-gray-900 count-up">$150</p>
<div class="mt-4 pt-4 border-t border-gray-100">
<button class="w-full py-2 monaco-gradient text-white rounded-lg font-semibold hover:shadow-lg transition-all">
Pay Now
</button>
</div>
</div>
</div>
<!-- Events Card -->
<div class="glass rounded-2xl p-6 card-hover relative overflow-hidden group">
<div class="absolute inset-0 monaco-gradient opacity-0 group-hover:opacity-10 transition-opacity"></div>
<div class="relative">
<div class="flex items-center justify-between mb-4">
<div class="w-12 h-12 rounded-xl bg-gradient-to-br from-indigo-400 to-indigo-600 flex items-center justify-center shadow-lg">
<i data-lucide="calendar-check" class="w-6 h-6 text-white"></i>
</div>
<span class="text-xs font-bold text-indigo-600 bg-indigo-100 px-3 py-1 rounded-full">THIS YEAR</span>
</div>
<p class="text-sm text-gray-500 mb-1">Events Attended</p>
<p class="text-3xl font-bold text-gray-900 count-up">12</p>
<div class="mt-4 pt-4 border-t border-gray-100">
<div class="flex items-center justify-between text-xs">
<span class="text-gray-500">Upcoming</span>
<span class="font-semibold text-gray-700">3 events</span>
</div>
</div>
</div>
</div>
<!-- Points Card -->
<div class="glass rounded-2xl p-6 card-hover relative overflow-hidden group">
<div class="absolute inset-0 monaco-gradient opacity-0 group-hover:opacity-10 transition-opacity"></div>
<div class="relative">
<div class="flex items-center justify-between mb-4">
<div class="w-12 h-12 rounded-xl bg-gradient-to-br from-purple-400 to-purple-600 flex items-center justify-center shadow-lg">
<i data-lucide="trophy" class="w-6 h-6 text-white"></i>
</div>
<span class="text-xs font-bold text-purple-600 bg-purple-100 px-3 py-1 rounded-full">TOP 10%</span>
</div>
<p class="text-sm text-gray-500 mb-1">Member Points</p>
<p class="text-3xl font-bold text-gray-900 count-up">2,450</p>
<div class="mt-4 pt-4 border-t border-gray-100">
<div class="w-full bg-gray-200 rounded-full h-2">
<div class="monaco-gradient h-2 rounded-full" style="width: 78%"></div>
</div>
<p class="text-xs text-gray-500 mt-2">550 points to Platinum</p>
</div>
</div>
</div>
</div>
<!-- Featured Event Banner -->
<div class="mb-8">
<div class="monaco-gradient rounded-3xl p-8 relative overflow-hidden glow-red">
<div class="absolute top-0 right-0 w-96 h-96 bg-white rounded-full blur-3xl opacity-10"></div>
<div class="relative z-10 flex items-center justify-between">
<div class="text-white">
<div class="flex items-center space-x-3 mb-3">
<div class="w-10 h-10 bg-white/20 backdrop-blur rounded-lg flex items-center justify-center">
<i data-lucide="sparkles" class="w-5 h-5"></i>
</div>
<span class="text-sm font-semibold bg-white/20 px-3 py-1 rounded-full">FEATURED EVENT</span>
</div>
<h3 class="text-3xl font-bold mb-2">Annual Monaco Gala 2024</h3>
<p class="text-white/90 mb-4 max-w-xl">Join us for an elegant evening celebrating Monaco's heritage and culture. Black tie event with dinner, entertainment, and networking.</p>
<div class="flex items-center space-x-6 text-sm">
<div class="flex items-center space-x-2">
<i data-lucide="calendar" class="w-4 h-4"></i>
<span>December 15, 2024</span>
</div>
<div class="flex items-center space-x-2">
<i data-lucide="clock" class="w-4 h-4"></i>
<span>7:00 PM - 11:00 PM</span>
</div>
<div class="flex items-center space-x-2">
<i data-lucide="map-pin" class="w-4 h-4"></i>
<span>Monaco Embassy, Washington DC</span>
</div>
</div>
</div>
<div class="flex flex-col space-y-3">
<button class="px-6 py-3 bg-white text-red-600 rounded-xl font-semibold hover:shadow-xl transition-all">
RSVP Now
</button>
<button class="px-6 py-3 bg-white/20 backdrop-blur text-white rounded-xl font-semibold hover:bg-white/30 transition-all">
View Details
</button>
</div>
</div>
</div>
</div>
<!-- Content Grid -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
<!-- Recent Activity Feed -->
<div class="lg:col-span-2">
<div class="glass rounded-2xl p-6">
<div class="flex items-center justify-between mb-6">
<h3 class="text-xl font-bold text-gray-900">Recent Activity</h3>
<button class="text-sm text-red-600 hover:text-red-700 font-medium">View All</button>
</div>
<div class="space-y-4">
<!-- Activity Item -->
<div class="flex items-start space-x-4 p-4 hover:bg-gray-50 rounded-xl transition-colors">
<div class="w-10 h-10 rounded-xl bg-green-100 flex items-center justify-center flex-shrink-0">
<i data-lucide="user-plus" class="w-5 h-5 text-green-600"></i>
</div>
<div class="flex-1">
<p class="text-sm font-medium text-gray-900">New member joined</p>
<p class="text-sm text-gray-600 mt-1">Sarah Johnson joined as a Silver member</p>
<p class="text-xs text-gray-400 mt-2">2 hours ago</p>
</div>
</div>
<!-- Activity Item -->
<div class="flex items-start space-x-4 p-4 hover:bg-gray-50 rounded-xl transition-colors">
<div class="w-10 h-10 rounded-xl bg-blue-100 flex items-center justify-center flex-shrink-0">
<i data-lucide="calendar" class="w-5 h-5 text-blue-600"></i>
</div>
<div class="flex-1">
<p class="text-sm font-medium text-gray-900">Event reminder</p>
<p class="text-sm text-gray-600 mt-1">Wine Tasting Event is coming up in 3 days</p>
<p class="text-xs text-gray-400 mt-2">5 hours ago</p>
</div>
</div>
<!-- Activity Item -->
<div class="flex items-start space-x-4 p-4 hover:bg-gray-50 rounded-xl transition-colors">
<div class="w-10 h-10 rounded-xl monaco-gradient flex items-center justify-center flex-shrink-0">
<i data-lucide="trophy" class="w-5 h-5 text-white"></i>
</div>
<div class="flex-1">
<p class="text-sm font-medium text-gray-900">Achievement unlocked!</p>
<p class="text-sm text-gray-600 mt-1">You've attended 10+ events this year</p>
<p class="text-xs text-gray-400 mt-2">1 day ago</p>
</div>
</div>
</div>
</div>
</div>
<!-- Member Spotlight -->
<div>
<div class="glass rounded-2xl p-6">
<h3 class="text-xl font-bold text-gray-900 mb-6">Member Spotlight</h3>
<div class="text-center">
<div class="relative inline-block mb-4">
<img src="https://ui-avatars.com/api/?name=John+Smith&background=dc2626&color=fff&size=128"
alt="Member" class="w-24 h-24 rounded-full ring-4 ring-red-100">
<div class="absolute bottom-0 right-0 w-7 h-7 bg-green-500 rounded-full border-3 border-white flex items-center justify-center">
<i data-lucide="check" class="w-4 h-4 text-white"></i>
</div>
</div>
<h4 class="text-lg font-semibold text-gray-900">John Smith</h4>
<p class="text-sm text-gray-500 mb-1">Board Member</p>
<div class="inline-flex items-center space-x-2 px-3 py-1 bg-gradient-to-r from-amber-400 to-orange-500 text-white rounded-full text-xs font-semibold mt-2">
<i data-lucide="award" class="w-3 h-3"></i>
<span>PLATINUM MEMBER</span>
</div>
<div class="mt-6 pt-6 border-t border-gray-100">
<div class="grid grid-cols-3 gap-4 text-center">
<div>
<p class="text-2xl font-bold text-gray-900">15</p>
<p class="text-xs text-gray-500">Years</p>
</div>
<div>
<p class="text-2xl font-bold text-gray-900">89</p>
<p class="text-xs text-gray-500">Events</p>
</div>
<div>
<p class="text-2xl font-bold text-gray-900">5.2k</p>
<p class="text-xs text-gray-500">Points</p>
</div>
</div>
</div>
<button class="w-full mt-6 px-4 py-2 monaco-gradient text-white rounded-lg font-semibold hover:shadow-lg transition-all">
View Profile
</button>
</div>
</div>
</div>
</div>
</main>
<!-- Floating Action Button -->
<div class="fixed bottom-8 right-8">
<button class="w-14 h-14 monaco-gradient rounded-full shadow-2xl hover:shadow-3xl transition-all glow-red-intense flex items-center justify-center group">
<i data-lucide="plus" class="w-6 h-6 text-white group-hover:rotate-45 transition-transform"></i>
</button>
</div>
<script>
lucide.createIcons();
// Add number animation on scroll
const observerOptions = {
threshold: 0.5
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('count-up');
}
});
}, observerOptions);
document.querySelectorAll('.count-up').forEach(el => {
observer.observe(el);
});
</script>
</body>
</html>

View File

@ -0,0 +1,472 @@
// ============================================
// Dashboard Component Styles
// Professional enhancements for all dashboards
// ============================================
// Dashboard Container
.admin-dashboard,
.board-dashboard,
.member-dashboard {
padding: 2rem;
min-height: 100vh;
background: linear-gradient(135deg, #fafafa 0%, #f4f4f5 100%);
@media (max-width: 768px) {
padding: 1rem;
}
}
// Enhanced Dashboard Header
.dashboard-header {
text-align: center;
padding: 3rem 2rem;
margin-bottom: 2rem;
position: relative;
overflow: hidden;
&.glass-header {
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(20px) saturate(180%);
-webkit-backdrop-filter: blur(20px) saturate(180%);
border: 1px solid rgba(255, 255, 255, 0.25);
border-radius: 24px;
box-shadow:
0 20px 40px rgba(0, 0, 0, 0.05),
inset 0 1px 0 rgba(255, 255, 255, 0.5);
&::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: radial-gradient(
circle,
rgba(220, 38, 38, 0.03) 0%,
transparent 70%
);
animation: float 20s ease-in-out infinite;
}
}
.dashboard-title {
font-size: 3rem;
font-weight: 800;
margin-bottom: 0.5rem;
letter-spacing: -0.02em;
&.text-gradient {
background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
@media (max-width: 768px) {
font-size: 2rem;
}
}
.dashboard-subtitle {
font-size: 1.125rem;
color: #64748b;
font-weight: 500;
}
}
// Enhanced Stat Cards
.stat-card {
height: 100%;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
.stat-value {
font-size: 2.5rem;
font-weight: 700;
color: #1f2937;
line-height: 1.2;
margin: 0.5rem 0;
@media (max-width: 768px) {
font-size: 2rem;
}
}
&:hover {
transform: translateY(-6px) scale(1.02);
box-shadow:
0 25px 50px rgba(0, 0, 0, 0.15),
0 10px 30px rgba(220, 38, 38, 0.1);
}
.v-avatar {
background: linear-gradient(135deg,
rgba(var(--v-theme-on-surface), 0.05) 0%,
rgba(var(--v-theme-on-surface), 0.02) 100%);
}
}
// Enhanced Glass Cards
.glass-card {
background: rgba(255, 255, 255, 0.88) !important;
backdrop-filter: blur(16px) saturate(180%) !important;
-webkit-backdrop-filter: blur(16px) saturate(180%) !important;
border: 1px solid rgba(255, 255, 255, 0.3) !important;
box-shadow:
0 10px 40px rgba(0, 0, 0, 0.08),
inset 0 1px 0 rgba(255, 255, 255, 0.6) !important;
&:hover {
box-shadow:
0 20px 60px rgba(0, 0, 0, 0.12),
inset 0 1px 0 rgba(255, 255, 255, 0.8) !important;
}
}
// Enhanced Bento Grid
.bento-grid {
display: grid !important;
grid-template-columns: repeat(12, 1fr) !important;
gap: 1.5rem !important;
margin-bottom: 2rem;
.bento-item {
position: relative;
&--small {
grid-column: span 3 !important;
@media (max-width: 1280px) {
grid-column: span 6 !important;
}
@media (max-width: 768px) {
grid-column: span 12 !important;
}
}
&--medium {
grid-column: span 4 !important;
@media (max-width: 1280px) {
grid-column: span 6 !important;
}
@media (max-width: 768px) {
grid-column: span 12 !important;
}
}
&--large {
grid-column: span 6 !important;
@media (max-width: 768px) {
grid-column: span 12 !important;
}
}
&--xlarge {
grid-column: span 8 !important;
@media (max-width: 1280px) {
grid-column: span 12 !important;
}
}
&--full {
grid-column: span 12 !important;
}
}
}
// Enhanced Data Tables
.v-data-table {
background: transparent !important;
.v-data-table__wrapper {
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
border-radius: 16px;
overflow: hidden;
}
thead {
background: linear-gradient(135deg,
rgba(220, 38, 38, 0.03) 0%,
rgba(185, 28, 28, 0.01) 100%);
th {
font-weight: 600 !important;
font-size: 0.75rem !important;
text-transform: uppercase;
letter-spacing: 0.05em;
color: #64748b !important;
padding: 1rem !important;
}
}
tbody {
tr {
transition: all 0.2s ease;
&:hover {
background: rgba(220, 38, 38, 0.02) !important;
td {
color: #1f2937 !important;
}
}
td {
padding: 1rem !important;
font-size: 0.875rem;
color: #475569;
}
}
}
}
// Enhanced Buttons in Dashboards
.dashboard-action-btn {
position: relative;
overflow: hidden;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
&::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border-radius: 50%;
background: rgba(255, 255, 255, 0.5);
transform: translate(-50%, -50%);
transition: width 0.6s, height 0.6s;
}
&:hover {
transform: translateY(-2px);
box-shadow: 0 10px 30px rgba(220, 38, 38, 0.2);
&::before {
width: 300px;
height: 300px;
}
}
}
// Activity Timeline Enhancement
.activity-timeline {
.v-timeline-item {
&::before {
background: linear-gradient(180deg,
rgba(220, 38, 38, 0.1) 0%,
transparent 100%);
}
.v-timeline-item__dot {
box-shadow: 0 4px 12px rgba(220, 38, 38, 0.2);
}
}
}
// Quick Actions Enhancement
.quick-actions-card {
.v-btn {
margin: 0.25rem;
&:hover {
transform: translateY(-2px);
}
}
}
// Enhanced Loading States
.skeleton-loader {
background: linear-gradient(
90deg,
rgba(255, 255, 255, 0.5) 25%,
rgba(255, 255, 255, 0.8) 50%,
rgba(255, 255, 255, 0.5) 75%
);
background-size: 200% 100%;
animation: loading 1.5s infinite;
}
@keyframes loading {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
@keyframes float {
0%, 100% {
transform: translate(0, 0) rotate(0deg);
}
33% {
transform: translate(30px, -30px) rotate(120deg);
}
66% {
transform: translate(-20px, 20px) rotate(240deg);
}
}
// Animated Entrance
.animated-entrance {
animation: slideInUp 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
@keyframes slideInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
// Professional Typography in Dashboards
.dashboard-section-title {
font-size: 1.5rem;
font-weight: 700;
color: #1f2937;
margin-bottom: 1.5rem;
position: relative;
padding-left: 1rem;
&::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 4px;
height: 24px;
background: linear-gradient(180deg, #dc2626 0%, #b91c1c 100%);
border-radius: 2px;
}
}
// Status Badges Enhancement
.status-badge {
padding: 0.25rem 0.75rem;
border-radius: 999px;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
display: inline-flex;
align-items: center;
gap: 0.25rem;
&--active {
background: linear-gradient(135deg,
rgba(34, 197, 94, 0.1) 0%,
rgba(34, 197, 94, 0.05) 100%);
color: #16a34a;
border: 1px solid rgba(34, 197, 94, 0.2);
}
&--pending {
background: linear-gradient(135deg,
rgba(245, 158, 11, 0.1) 0%,
rgba(245, 158, 11, 0.05) 100%);
color: #ca8a04;
border: 1px solid rgba(245, 158, 11, 0.2);
}
&--inactive {
background: linear-gradient(135deg,
rgba(107, 114, 128, 0.1) 0%,
rgba(107, 114, 128, 0.05) 100%);
color: #6b7280;
border: 1px solid rgba(107, 114, 128, 0.2);
}
}
// Chart Card Enhancement
.chart-card {
.chart-header {
padding: 1.5rem;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
.chart-title {
font-size: 1.125rem;
font-weight: 600;
color: #1f2937;
}
.chart-subtitle {
font-size: 0.875rem;
color: #64748b;
margin-top: 0.25rem;
}
}
.chart-body {
padding: 1.5rem;
position: relative;
}
}
// Responsive Improvements
@media (max-width: 768px) {
.dashboard-header {
padding: 2rem 1rem;
.dashboard-title {
font-size: 1.75rem;
}
.dashboard-subtitle {
font-size: 1rem;
}
}
.bento-grid {
gap: 1rem !important;
}
.stat-card {
.stat-value {
font-size: 1.75rem;
}
}
}
// Dark Mode Support
@media (prefers-color-scheme: dark) {
.admin-dashboard,
.board-dashboard,
.member-dashboard {
background: linear-gradient(135deg, #18181b 0%, #27272a 100%);
}
.dashboard-header.glass-header {
background: rgba(30, 30, 30, 0.9);
border-color: rgba(255, 255, 255, 0.1);
}
.glass-card {
background: rgba(30, 30, 30, 0.88) !important;
border-color: rgba(255, 255, 255, 0.1) !important;
}
.dashboard-title.text-gradient {
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.stat-value,
.dashboard-section-title {
color: #f4f4f5;
}
}

View File

@ -0,0 +1,548 @@
// Monaco USA Portal - Design System v2.0
// Addressing critical issues from visual audit
// ============================================
// 1. COLOR PALETTE - Standardized
// ============================================
// Brand Colors
$monaco-red: #DC143C;
$monaco-red-dark: #B91C3C;
$monaco-red-light: #FF6B8A;
$monaco-white: #FFFFFF;
$monaco-gold: #FFD700;
// Semantic Colors
$color-success: #10B981;
$color-warning: #F59E0B;
$color-error: #EF4444;
$color-info: #3B82F6;
// Neutral Palette
$neutral-900: #0F172A;
$neutral-800: #1E293B;
$neutral-700: #334155;
$neutral-600: #475569;
$neutral-500: #64748B;
$neutral-400: #94A3B8;
$neutral-300: #CBD5E1;
$neutral-200: #E2E8F0;
$neutral-100: #F1F5F9;
$neutral-50: #F8FAFC;
// Glass Morphism
$glass-white: rgba(255, 255, 255, 0.1);
$glass-white-hover: rgba(255, 255, 255, 0.15);
$glass-border: rgba(255, 255, 255, 0.2);
$glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
// ============================================
// 2. TYPOGRAPHY - Consistent Hierarchy
// ============================================
// Font Family
$font-primary: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
$font-mono: 'Fira Code', 'Monaco', monospace;
// Font Sizes - Using rem for accessibility
$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
// Line Heights
$leading-none: 1;
$leading-tight: 1.2;
$leading-snug: 1.375;
$leading-normal: 1.6;
$leading-relaxed: 1.75;
$leading-loose: 2;
// Font Weights
$font-light: 300;
$font-regular: 400;
$font-medium: 500;
$font-semibold: 600;
$font-bold: 700;
$font-extrabold: 800;
// ============================================
// 3. SPACING SYSTEM - 8px Grid
// ============================================
$space-px: 1px;
$space-0: 0;
$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-7: 1.75rem; // 28px
$space-8: 2rem; // 32px
$space-10: 2.5rem; // 40px
$space-12: 3rem; // 48px
$space-16: 4rem; // 64px
$space-20: 5rem; // 80px
$space-24: 6rem; // 96px
// ============================================
// 4. BORDER RADIUS - Consistent Curves
// ============================================
$radius-none: 0;
$radius-sm: 0.25rem; // 4px
$radius-md: 0.5rem; // 8px
$radius-lg: 0.75rem; // 12px
$radius-xl: 1rem; // 16px
$radius-2xl: 1.5rem; // 24px
$radius-full: 9999px;
// ============================================
// 5. SHADOWS - Depth System
// ============================================
$shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
$shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07);
$shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
$shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.15);
$shadow-2xl: 0 25px 50px rgba(0, 0, 0, 0.25);
$shadow-inner: inset 0 2px 4px rgba(0, 0, 0, 0.06);
$shadow-glass: 0 8px 32px rgba(0, 0, 0, 0.1);
// ============================================
// 6. BREAKPOINTS - Mobile First
// ============================================
$breakpoint-sm: 640px;
$breakpoint-md: 768px;
$breakpoint-lg: 1024px;
$breakpoint-xl: 1280px;
$breakpoint-2xl: 1536px;
@mixin sm {
@media (min-width: $breakpoint-sm) { @content; }
}
@mixin md {
@media (min-width: $breakpoint-md) { @content; }
}
@mixin lg {
@media (min-width: $breakpoint-lg) { @content; }
}
@mixin xl {
@media (min-width: $breakpoint-xl) { @content; }
}
@mixin xxl {
@media (min-width: $breakpoint-2xl) { @content; }
}
// ============================================
// 7. TRANSITIONS - Smooth Interactions
// ============================================
$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);
$ease-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
$duration-fast: 150ms;
$duration-normal: 250ms;
$duration-slow: 350ms;
$duration-slower: 500ms;
// ============================================
// 8. Z-INDEX SCALE - Layering System
// ============================================
$z-negative: -1;
$z-0: 0;
$z-10: 10;
$z-20: 20;
$z-30: 30;
$z-40: 40;
$z-50: 50;
$z-dropdown: 1000;
$z-sticky: 1020;
$z-fixed: 1030;
$z-modal-backdrop: 1040;
$z-modal: 1050;
$z-popover: 1060;
$z-tooltip: 1070;
$z-notification: 1080;
// ============================================
// 9. IMPROVED GLASS EFFECT MIXIN
// ============================================
@mixin glass-effect(
$blur: 10px,
$opacity: 0.1,
$border-opacity: 0.2,
$shadow: true
) {
background: rgba(255, 255, 255, $opacity);
@supports (backdrop-filter: blur($blur)) or (-webkit-backdrop-filter: blur($blur)) {
backdrop-filter: blur($blur);
-webkit-backdrop-filter: blur($blur);
}
border: 1px solid rgba(255, 255, 255, $border-opacity);
@if $shadow {
box-shadow: $shadow-glass;
}
transition: all $duration-normal $ease-out;
&:hover {
background: rgba(255, 255, 255, $opacity + 0.05);
border-color: rgba(255, 255, 255, $border-opacity + 0.1);
@if $shadow {
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
}
}
}
// ============================================
// 10. COMPONENT CLASSES - Reusable Styles
// ============================================
// Cards
.card-base {
@include glass-effect(12px, 0.08, 0.18, true);
border-radius: $radius-xl;
padding: $space-6;
margin-bottom: $space-4;
@include md {
padding: $space-8;
}
}
// Buttons
@mixin button-base {
display: inline-flex;
align-items: center;
justify-content: center;
gap: $space-2;
padding: $space-3 $space-6;
border-radius: $radius-lg;
font-weight: $font-medium;
transition: all $duration-normal $ease-out;
cursor: pointer;
border: 1px solid transparent;
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
&:focus-visible {
outline: 2px solid $monaco-red;
outline-offset: 2px;
}
}
.btn-primary {
@include button-base;
background: linear-gradient(135deg, $monaco-red 0%, $monaco-red-dark 100%);
color: white;
&:hover:not(:disabled) {
transform: translateY(-2px);
box-shadow: 0 10px 20px rgba($monaco-red, 0.3);
}
&:active:not(:disabled) {
transform: translateY(0);
}
}
.btn-secondary {
@include button-base;
background: $neutral-100;
color: $neutral-800;
border-color: $neutral-300;
&:hover:not(:disabled) {
background: $neutral-200;
border-color: $neutral-400;
}
}
.btn-ghost {
@include button-base;
background: transparent;
color: $neutral-600;
&:hover:not(:disabled) {
background: $neutral-100;
color: $neutral-800;
}
}
// Typography Classes
.heading-1 {
font-size: $text-4xl;
font-weight: $font-bold;
line-height: $leading-tight;
color: $neutral-900;
@include md {
font-size: $text-5xl;
}
}
.heading-2 {
font-size: $text-3xl;
font-weight: $font-semibold;
line-height: $leading-tight;
color: $neutral-900;
@include md {
font-size: $text-4xl;
}
}
.heading-3 {
font-size: $text-2xl;
font-weight: $font-semibold;
line-height: $leading-snug;
color: $neutral-800;
}
.heading-4 {
font-size: $text-xl;
font-weight: $font-medium;
line-height: $leading-snug;
color: $neutral-800;
}
.body-text {
font-size: $text-base;
line-height: $leading-normal;
color: $neutral-700;
}
.small-text {
font-size: $text-sm;
line-height: $leading-normal;
color: $neutral-600;
}
// ============================================
// 11. LAYOUT UTILITIES
// ============================================
.container {
width: 100%;
max-width: 1280px;
margin: 0 auto;
padding: 0 $space-4;
@include md {
padding: 0 $space-6;
}
@include lg {
padding: 0 $space-8;
}
}
.grid {
display: grid;
gap: $space-4;
&.grid-cols-1 {
grid-template-columns: repeat(1, 1fr);
}
@include md {
&.md\:grid-cols-2 {
grid-template-columns: repeat(2, 1fr);
}
}
@include lg {
&.lg\:grid-cols-3 {
grid-template-columns: repeat(3, 1fr);
}
&.lg\:grid-cols-4 {
grid-template-columns: repeat(4, 1fr);
}
}
}
.flex {
display: flex;
&.flex-col {
flex-direction: column;
}
&.items-center {
align-items: center;
}
&.justify-between {
justify-content: space-between;
}
&.gap-2 {
gap: $space-2;
}
&.gap-4 {
gap: $space-4;
}
}
// ============================================
// 12. ANIMATION CLASSES
// ============================================
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideIn {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0);
}
}
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
.animate-fadeIn {
animation: fadeIn $duration-normal $ease-out;
}
.animate-slideIn {
animation: slideIn $duration-slow $ease-out;
}
.animate-pulse {
animation: pulse 2s $ease-in-out infinite;
}
// ============================================
// 13. ACCESSIBILITY UTILITIES
// ============================================
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
.focus-visible {
&:focus-visible {
outline: 2px solid $monaco-red;
outline-offset: 2px;
border-radius: $radius-sm;
}
}
// ============================================
// 14. STATUS INDICATORS
// ============================================
.status-badge {
display: inline-flex;
align-items: center;
padding: $space-1 $space-3;
border-radius: $radius-full;
font-size: $text-xs;
font-weight: $font-semibold;
text-transform: uppercase;
letter-spacing: 0.05em;
&.status-overdue {
background: rgba($color-error, 0.1);
color: $color-error;
border: 1px solid rgba($color-error, 0.2);
}
&.status-pending {
background: rgba($color-warning, 0.1);
color: $color-warning;
border: 1px solid rgba($color-warning, 0.2);
}
&.status-paid {
background: rgba($color-success, 0.1);
color: $color-success;
border: 1px solid rgba($color-success, 0.2);
}
}
// ============================================
// 15. LOADING STATES
// ============================================
.skeleton {
background: linear-gradient(
90deg,
$neutral-200 25%,
$neutral-100 50%,
$neutral-200 75%
);
background-size: 200% 100%;
animation: loading 1.5s infinite;
border-radius: $radius-md;
&.skeleton-text {
height: $space-4;
margin-bottom: $space-2;
}
&.skeleton-card {
height: 120px;
margin-bottom: $space-4;
}
}
@keyframes loading {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}

View File

@ -1,6 +1,9 @@
// MonacoUSA Portal - Main Stylesheet
// Based on design-system.md specifications
// Import component styles
@import 'components/dashboards';
// ============================================
// 1. Variables & Design Tokens
// ============================================

View File

@ -1,690 +0,0 @@
# MonacoUSA Portal Design System V2
## Professional Neumorphic & Glassmorphic Design Language
---
## Table of Contents
1. [Design Philosophy](#design-philosophy)
2. [Color System](#color-system)
3. [Typography](#typography)
4. [Spacing & Layout](#spacing--layout)
5. [Shadow System](#shadow-system)
6. [Component Library](#component-library)
7. [Morphing Dropdown Specification](#morphing-dropdown-specification)
8. [Animation Guidelines](#animation-guidelines)
9. [Responsive Design](#responsive-design)
10. [Implementation Examples](#implementation-examples)
---
## Design Philosophy
### Core Principles
- **Professional & Inviting**: Balancing corporate credibility with approachable design
- **Neumorphic Foundation**: Soft UI with subtle depth and tactile feedback
- **Glassmorphic Accents**: Strategic use of transparency and blur for interactive elements
- **Monaco Heritage**: Incorporating national colors while maintaining modern aesthetics
### Visual Hierarchy
1. **Primary Actions**: Monaco Red gradient buttons with strong shadows
2. **Interactive Elements**: Blue glassmorphic dropdowns and selects
3. **Content Cards**: Neumorphic white/light gray surfaces
4. **Navigation**: Subtle depth with hover transformations
---
## Color System
### Primary Palette - Monaco Red
```scss
$primary-50: #FEF2F2; // Lightest tint
$primary-100: #FEE2E2;
$primary-200: #FECACA;
$primary-300: #FCA5A5;
$primary-400: #F87171;
$primary-500: #EF4444;
$primary-600: #CC0000; // Monaco Red (Main)
$primary-700: #990000; // Monaco Dark Red
$primary-800: #660000;
$primary-900: #450A0A; // Darkest shade
```
### Secondary Palette - Interactive Blue
Used exclusively for dropdowns, selects, and interactive overlays:
```scss
$blue-50: #EFF6FF;
$blue-100: #DBEAFE;
$blue-200: #BFDBFE;
$blue-300: #93C5FD;
$blue-400: #60A5FA;
$blue-500: #3B82F6;
$blue-600: #2563EB;
$blue-700: #1D4ED8;
$blue-800: #1E40AF;
$blue-900: #1E3A8A;
```
### Neutral Palette
```scss
$neutral-50: #F9FAFB;
$neutral-100: #F3F4F6;
$neutral-200: #E5E7EB;
$neutral-300: #D1D5DB;
$neutral-400: #9CA3AF;
$neutral-500: #6B7280;
$neutral-600: #4B5563;
$neutral-700: #374151;
$neutral-800: #1F2937;
$neutral-900: #111827;
```
### Semantic Colors
```scss
$success: #10B981; // Green
$warning: #F59E0B; // Amber
$error: #EF4444; // Red
$info: #3B82F6; // Blue
```
---
## Typography
### Font Stack
```css
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Inter', Roboto,
'Helvetica Neue', Arial, sans-serif;
```
### Type Scale
| Name | Size | Line Height | Usage |
|------|------|-------------|-------|
| `text-xs` | 0.75rem (12px) | 1.5 | Labels, captions |
| `text-sm` | 0.875rem (14px) | 1.5 | Body small, form labels |
| `text-base` | 1rem (16px) | 1.5 | Body default |
| `text-lg` | 1.125rem (18px) | 1.625 | Body large |
| `text-xl` | 1.25rem (20px) | 1.625 | H4 |
| `text-2xl` | 1.5rem (24px) | 1.375 | H3 |
| `text-3xl` | 1.875rem (30px) | 1.375 | H2 |
| `text-4xl` | 2.25rem (36px) | 1.25 | H1 |
### Font Weights
- `font-normal`: 400 - Body text
- `font-medium`: 500 - Emphasis
- `font-semibold`: 600 - Subheadings
- `font-bold`: 700 - Headings
---
## Spacing & Layout
### Spacing Scale
```scss
$space-0: 0;
$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
```
### Border Radius
```scss
$radius-sm: 0.375rem; // 6px - Small elements
$radius-md: 0.5rem; // 8px - Buttons
$radius-lg: 0.75rem; // 12px - Cards, dropdowns
$radius-xl: 1rem; // 16px - Large cards
$radius-2xl: 1.5rem; // 24px - Hero sections
$radius-full: 9999px; // Pills, avatars
```
---
## Shadow System
### Neumorphic Shadows
For cards and static elements:
```scss
// Soft elevation shadows
$shadow-soft-sm:
4px 4px 8px rgba(0, 0, 0, 0.08),
-4px -4px 8px rgba(255, 255, 255, 0.95);
$shadow-soft-md:
8px 8px 16px rgba(0, 0, 0, 0.1),
-8px -8px 16px rgba(255, 255, 255, 0.95);
$shadow-soft-lg:
12px 12px 24px rgba(0, 0, 0, 0.12),
-12px -12px 24px rgba(255, 255, 255, 0.95);
// Inset shadows for pressed states
$shadow-inset-sm:
inset 4px 4px 8px rgba(0, 0, 0, 0.1),
inset -4px -4px 8px rgba(255, 255, 255, 0.95);
```
### Glassmorphic Shadows
For dropdowns and overlays:
```scss
$shadow-morphing:
0 10px 40px rgba(0, 0, 0, 0.12),
0 2px 10px rgba(0, 0, 0, 0.08);
$shadow-glass:
0 8px 32px rgba(0, 0, 0, 0.1),
0 0 0 1px rgba(255, 255, 255, 0.3);
```
---
## Component Library
### 1. Buttons
#### Primary Button
```scss
.btn-primary {
background: linear-gradient(135deg, $primary-600, $primary-700);
color: white;
padding: 0.75rem 1.5rem;
border-radius: $radius-lg;
box-shadow:
4px 4px 8px rgba(204, 0, 0, 0.3),
-4px -4px 8px rgba(255, 255, 255, 0.95);
&:hover {
transform: translateY(-2px);
box-shadow:
6px 6px 12px rgba(204, 0, 0, 0.4),
-6px -6px 12px rgba(255, 255, 255, 0.95);
}
&:active {
transform: translateY(0);
box-shadow: $shadow-inset-sm;
}
}
```
#### Neumorphic Button
```scss
.btn-neumorphic {
background: linear-gradient(145deg, #ffffff, #f0f0f0);
color: $neutral-700;
padding: 0.75rem 1.5rem;
border-radius: $radius-lg;
box-shadow: $shadow-soft-sm;
&:hover {
box-shadow: $shadow-soft-md;
transform: translateY(-1px);
}
}
```
### 2. Cards
#### Neumorphic Card
```scss
.card-neumorphic {
background: linear-gradient(145deg, #ffffff, #f0f0f0);
border-radius: $radius-xl;
padding: $space-6;
box-shadow: $shadow-soft-md;
&:hover {
transform: translateY(-4px);
box-shadow: $shadow-soft-lg;
}
}
```
### 3. Form Inputs
#### Neumorphic Input
```scss
.input-neumorphic {
background: $neutral-50;
border: none;
border-radius: $radius-lg;
padding: $space-3 $space-4;
box-shadow: $shadow-inset-sm;
&:focus {
outline: none;
box-shadow:
$shadow-inset-md,
0 0 0 3px rgba($primary-500, 0.1);
}
}
```
---
## Morphing Dropdown Specification
### Overview
The morphing dropdown is a signature component that combines glassmorphism with smooth spring animations. It features a blue color scheme distinct from the primary Monaco red, creating clear visual hierarchy for interactive elements.
### Visual Characteristics
#### Trigger State
```scss
.morphing-select-trigger {
// Base styling
height: 48px;
padding: 0 1rem;
border-radius: 12px;
// Glassmorphic background
background: rgba(239, 246, 255, 0.3); // blue-50 with 30% opacity
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
border: 1px solid rgba(191, 219, 254, 0.3); // blue-200 with 30% opacity
// Typography
color: #1E3A8A; // blue-900
font-size: 0.875rem;
// Transition
transition: all 0.3s ease;
&:hover {
background: rgba(219, 234, 254, 0.4); // blue-100 with 40% opacity
border-color: rgba(147, 197, 253, 0.4); // blue-300 with 40% opacity
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.1);
}
&.open {
background: rgba(219, 234, 254, 0.5);
border-color: rgba(96, 165, 250, 0.5); // blue-400 with 50% opacity
box-shadow:
0 0 0 3px rgba(59, 130, 246, 0.1),
0 4px 12px rgba(59, 130, 246, 0.15);
}
}
```
#### Dropdown Panel
```scss
.morphing-select-dropdown {
// Glassmorphic container
background: rgba(239, 246, 255, 0.95); // blue-50 with 95% opacity
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border: 1px solid rgba(191, 219, 254, 0.3);
border-radius: 12px;
// Shadow
box-shadow:
0 10px 40px rgba(0, 0, 0, 0.12),
0 2px 10px rgba(0, 0, 0, 0.08);
// Animation
animation: morphIn 0.3s cubic-bezier(0.4, 0, 0.2, 1);
// Layout
max-height: 300px;
overflow: hidden;
}
```
#### Option Items
```scss
.morphing-select-option {
padding: 0.625rem 0.75rem;
border-radius: 8px;
color: #1E3A8A; // blue-900
font-size: 0.875rem;
transition: all 0.2s ease;
&:hover {
background: rgba(96, 165, 250, 0.2); // blue-400 with 20% opacity
transform: translateX(2px);
}
&.selected {
background: rgba(59, 130, 246, 0.25); // blue-500 with 25% opacity
font-weight: 500;
}
}
```
#### Option Groups
```scss
.morphing-select-optgroup-label {
padding: 0.5rem 0.75rem 0.25rem;
font-size: 0.75rem;
font-weight: 600;
color: #1D4ED8; // blue-700
text-transform: uppercase;
letter-spacing: 0.05em;
opacity: 0.8;
}
```
### Animation Specifications
#### Spring Configuration
```javascript
const TRANSITION = {
type: 'spring',
bounce: 0.05, // Minimal bounce for professional feel
duration: 0.3, // Quick but smooth
};
```
#### Animation States
```javascript
// Opening animation
@keyframes morphIn {
from {
opacity: 0;
transform: scale(0.95) translateY(-10px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}
// Closing animation (reversed)
@keyframes morphOut {
from {
opacity: 1;
transform: scale(1) translateY(0);
}
to {
opacity: 0;
transform: scale(0.95) translateY(-10px);
}
}
```
### Interaction Patterns
#### Click Outside
- Clicking anywhere outside the dropdown closes it
- Uses `mousedown` event for immediate response
#### Keyboard Navigation
- `Escape` key closes the dropdown
- `Tab` key navigates through options
- `Enter` selects the focused option
- `Space` toggles selection in multi-select
#### Touch Support
- Touch events are handled identically to mouse events
- Larger touch targets on mobile (min 44px)
### Multi-Select Variant
#### Selected Tags
```scss
.morphing-select-tag {
display: inline-flex;
align-items: center;
gap: 0.25rem;
padding: 0.25rem 0.5rem;
background: rgba(191, 219, 254, 0.6); // blue-200 with 60% opacity
border-radius: 6px;
font-size: 0.75rem;
// Remove button
.tag-remove {
color: #1D4ED8; // blue-700
cursor: pointer;
&:hover {
color: #1E3A8A; // blue-900
}
}
}
```
### Accessibility Features
- ARIA attributes: `aria-expanded`, `aria-controls`, `aria-modal`
- Role attributes: `role="dialog"` for dropdown panel
- Keyboard navigation support
- Focus management
- Screen reader announcements
### Implementation Guidelines
#### Vue/React Component Structure
```typescript
interface SelectOption {
value: string;
label: string;
disabled?: boolean;
}
interface OptionGroup {
label: string;
options: SelectOption[];
}
interface SelectProps {
options?: SelectOption[];
optgroups?: OptionGroup[];
value?: string | string[]; // string[] for multi-select
onValueChange?: (value: string | string[]) => void;
placeholder?: string;
disabled?: boolean;
maxSelected?: number; // For multi-select
className?: string;
}
```
#### State Management
```javascript
// Single Select
const [selectedValue, setSelectedValue] = useState('');
const [isOpen, setIsOpen] = useState(false);
// Multi-Select
const [selectedValues, setSelectedValues] = useState([]);
const [isOpen, setIsOpen] = useState(false);
```
---
## Animation Guidelines
### Timing Functions
```scss
$ease-smooth: cubic-bezier(0.4, 0, 0.2, 1);
$ease-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
$ease-out: cubic-bezier(0, 0, 0.2, 1);
$ease-in-out: cubic-bezier(0.4, 0, 0.6, 1);
```
### Duration Scale
- `fast`: 150ms - Micro interactions
- `base`: 300ms - Standard transitions
- `slow`: 500ms - Complex animations
- `slower`: 700ms - Page transitions
### Animation Principles
1. **Purpose**: Every animation should have a clear purpose
2. **Performance**: Use `transform` and `opacity` for GPU acceleration
3. **Consistency**: Maintain consistent timing across similar interactions
4. **Subtlety**: Animations should enhance, not distract
---
## Responsive Design
### Breakpoints
```scss
$breakpoint-sm: 640px; // Mobile landscape
$breakpoint-md: 768px; // Tablet portrait
$breakpoint-lg: 1024px; // Tablet landscape
$breakpoint-xl: 1280px; // Desktop
$breakpoint-2xl: 1536px; // Large desktop
```
### Mobile Adaptations
#### Morphing Dropdown
- Full-width on mobile
- Bottom sheet presentation option
- Larger touch targets (min 44px)
- Reduced blur for performance
#### Navigation
- Hamburger menu below tablet
- Full-screen overlay navigation
- Swipe gestures for sidebar
#### Cards
- Single column layout on mobile
- Reduced padding and margins
- Simplified shadows for performance
---
## Implementation Examples
### Example 1: Login Form with Morphing Select
```html
<div class="login-form">
<!-- Email Input (Neumorphic) -->
<div class="form-group">
<label class="form-label">Email</label>
<input type="email" class="input-neumorphic" placeholder="you@example.com">
</div>
<!-- Role Selector (Morphing Dropdown) -->
<div class="form-group">
<label class="form-label">Select Role</label>
<div class="morphing-select">
<div class="morphing-select-trigger">
<span>Choose your role...</span>
<svg class="chevron">...</svg>
</div>
<div class="morphing-select-dropdown">
<div class="morphing-select-option">Member</div>
<div class="morphing-select-option">Admin</div>
<div class="morphing-select-option">Board Member</div>
</div>
</div>
</div>
<!-- Submit Button (Primary) -->
<button class="btn-primary">Sign In</button>
</div>
```
### Example 2: Dashboard Stats with Dropdown Filter
```html
<div class="dashboard-header">
<h1 class="page-title">Dashboard</h1>
<!-- Time Period Filter (Morphing Dropdown) -->
<div class="morphing-select">
<div class="morphing-select-trigger">
<span>Last 30 Days</span>
<svg class="chevron">...</svg>
</div>
<div class="morphing-select-dropdown">
<div class="morphing-select-optgroup">
<div class="morphing-select-optgroup-label">Quick Ranges</div>
<div class="morphing-select-option">Today</div>
<div class="morphing-select-option">Last 7 Days</div>
<div class="morphing-select-option selected">Last 30 Days</div>
</div>
<div class="morphing-select-optgroup">
<div class="morphing-select-optgroup-label">Custom</div>
<div class="morphing-select-option">Last Quarter</div>
<div class="morphing-select-option">Last Year</div>
</div>
</div>
</div>
</div>
<!-- Stats Cards (Neumorphic) -->
<div class="stats-grid">
<div class="card-neumorphic stat-card">
<div class="stat-value">2,847</div>
<div class="stat-label">Total Members</div>
<div class="stat-change positive">+12.5%</div>
</div>
<!-- More cards... -->
</div>
```
---
## Design Tokens
For implementation in code, use these design tokens:
```javascript
const designTokens = {
colors: {
primary: {
50: '#FEF2F2',
100: '#FEE2E2',
200: '#FECACA',
300: '#FCA5A5',
400: '#F87171',
500: '#EF4444',
600: '#CC0000', // Main brand color
700: '#990000',
800: '#660000',
900: '#450A0A',
},
blue: {
50: '#EFF6FF',
100: '#DBEAFE',
200: '#BFDBFE',
300: '#93C5FD',
400: '#60A5FA',
500: '#3B82F6',
600: '#2563EB',
700: '#1D4ED8',
800: '#1E40AF',
900: '#1E3A8A',
},
// ... other colors
},
shadows: {
'soft-sm': '4px 4px 8px rgba(0, 0, 0, 0.08), -4px -4px 8px rgba(255, 255, 255, 0.95)',
'soft-md': '8px 8px 16px rgba(0, 0, 0, 0.1), -8px -8px 16px rgba(255, 255, 255, 0.95)',
'soft-lg': '12px 12px 24px rgba(0, 0, 0, 0.12), -12px -12px 24px rgba(255, 255, 255, 0.95)',
'morphing': '0 10px 40px rgba(0, 0, 0, 0.12), 0 2px 10px rgba(0, 0, 0, 0.08)',
},
animation: {
duration: {
fast: '150ms',
base: '300ms',
slow: '500ms',
},
ease: {
smooth: 'cubic-bezier(0.4, 0, 0.2, 1)',
bounce: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)',
},
},
};
```
---
## Version History
- **v2.0.0** - Initial release with Neumorphic & Glassmorphic design system
- **v2.0.1** - Added morphing dropdown specifications
- **v2.0.2** - Enhanced mobile responsive guidelines
---
## Contact
For questions about implementing this design system, please contact the MonacoUSA Portal development team.

View File

@ -1,215 +0,0 @@
<template>
<div
:class="[
'neumorphic-card',
sizeClasses,
elevationClasses,
{
'cursor-pointer': clickable,
'transition-all duration-300': animated
}
]"
@click="handleClick"
>
<div v-if="$slots.header || title" class="neumorphic-card__header">
<slot name="header">
<h3 v-if="title" class="neumorphic-card__title">{{ title }}</h3>
<p v-if="subtitle" class="neumorphic-card__subtitle">{{ subtitle }}</p>
</slot>
</div>
<div class="neumorphic-card__content">
<slot></slot>
</div>
<div v-if="$slots.footer" class="neumorphic-card__footer">
<slot name="footer"></slot>
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue';
interface Props {
title?: string;
subtitle?: string;
size?: 'sm' | 'md' | 'lg' | 'xl';
elevation?: 'none' | 'sm' | 'md' | 'lg' | 'xl';
clickable?: boolean;
animated?: boolean;
pressed?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
size: 'md',
elevation: 'md',
clickable: false,
animated: true,
pressed: false
});
const emit = defineEmits<{
click: [event: MouseEvent];
}>();
const sizeClasses = computed(() => {
const sizes = {
sm: 'p-4',
md: 'p-6',
lg: 'p-8',
xl: 'p-10'
};
return sizes[props.size];
});
const elevationClasses = computed(() => {
if (props.pressed) return 'neumorphic-pressed';
const elevations = {
none: '',
sm: 'neumorphic-elevation-sm',
md: 'neumorphic-elevation-md',
lg: 'neumorphic-elevation-lg',
xl: 'neumorphic-elevation-xl'
};
return elevations[props.elevation];
});
const handleClick = (event: MouseEvent) => {
if (props.clickable) {
emit('click', event);
}
};
</script>
<style lang="scss" scoped>
@import '../../styles/neumorphic-system.scss';
.neumorphic-card {
background: linear-gradient(145deg, #ffffff, #f0f0f0);
border-radius: $radius-xl;
position: relative;
overflow: hidden;
// Base elevation styles
&.neumorphic-elevation-sm {
box-shadow: $shadow-soft-sm;
}
&.neumorphic-elevation-md {
box-shadow: $shadow-soft-md;
}
&.neumorphic-elevation-lg {
box-shadow: $shadow-soft-lg;
}
&.neumorphic-elevation-xl {
box-shadow: $shadow-soft-xl;
}
&.neumorphic-pressed {
box-shadow: $shadow-inset-md;
background: linear-gradient(145deg, #e6e6e6, #ffffff);
}
// Hover effects for clickable cards
&.cursor-pointer {
&:hover {
transform: translateY(-2px);
box-shadow: $shadow-soft-lg;
}
&:active {
transform: translateY(0);
box-shadow: $shadow-inset-sm;
}
}
// Header section
&__header {
padding-bottom: $space-4;
border-bottom: 1px solid rgba($neutral-300, 0.5);
margin-bottom: $space-4;
}
&__title {
font-family: $font-heading;
font-size: $text-xl;
font-weight: $font-semibold;
color: $neutral-800;
margin: 0;
line-height: $leading-tight;
}
&__subtitle {
font-family: $font-body;
font-size: $text-sm;
color: $neutral-600;
margin-top: $space-2;
line-height: $leading-normal;
}
// Content section
&__content {
font-family: $font-body;
color: $neutral-700;
line-height: $leading-relaxed;
}
// Footer section
&__footer {
padding-top: $space-4;
border-top: 1px solid rgba($neutral-300, 0.5);
margin-top: $space-4;
}
// Dark mode support
@include dark-mode {
background: linear-gradient(145deg, $neutral-800, $neutral-700);
&.neumorphic-elevation-sm {
box-shadow: $shadow-dark-soft-sm;
}
&.neumorphic-elevation-md {
box-shadow: $shadow-dark-soft-md;
}
&.neumorphic-elevation-lg {
box-shadow: $shadow-dark-soft-lg;
}
&.neumorphic-pressed {
box-shadow: $shadow-dark-inset-md;
background: linear-gradient(145deg, $neutral-900, $neutral-800);
}
.neumorphic-card__title {
color: $neutral-100;
}
.neumorphic-card__subtitle {
color: $neutral-400;
}
.neumorphic-card__content {
color: $neutral-300;
}
.neumorphic-card__header,
.neumorphic-card__footer {
border-color: rgba($neutral-600, 0.5);
}
}
// Responsive adjustments
@include responsive('sm') {
&.p-4 { padding: $space-5; }
&.p-6 { padding: $space-8; }
&.p-8 { padding: $space-10; }
&.p-10 { padding: $space-12; }
}
}
</style>

View File

@ -1,315 +0,0 @@
<template>
<button
:class="[
'professional-button',
variantClasses,
sizeClasses,
{
'professional-button--loading': loading,
'professional-button--disabled': disabled || loading,
'professional-button--block': block,
'professional-button--icon-only': !$slots.default && icon
}
]"
:disabled="disabled || loading"
@click="handleClick"
>
<span v-if="loading" class="professional-button__spinner">
<svg class="animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
</span>
<span v-if="icon && !loading" class="professional-button__icon">
<component :is="icon" />
</span>
<span v-if="$slots.default" class="professional-button__content">
<slot></slot>
</span>
<span v-if="endIcon && !loading" class="professional-button__end-icon">
<component :is="endIcon" />
</span>
</button>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import type { Component } from 'vue';
interface Props {
variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'danger';
size?: 'sm' | 'md' | 'lg';
loading?: boolean;
disabled?: boolean;
block?: boolean;
icon?: Component;
endIcon?: Component;
}
const props = withDefaults(defineProps<Props>(), {
variant: 'primary',
size: 'md',
loading: false,
disabled: false,
block: false
});
const emit = defineEmits<{
click: [event: MouseEvent];
}>();
const variantClasses = computed(() => {
const variants = {
primary: 'professional-button--primary',
secondary: 'professional-button--secondary',
outline: 'professional-button--outline',
ghost: 'professional-button--ghost',
danger: 'professional-button--danger'
};
return variants[props.variant];
});
const sizeClasses = computed(() => {
const sizes = {
sm: 'professional-button--sm',
md: 'professional-button--md',
lg: 'professional-button--lg'
};
return sizes[props.size];
});
const handleClick = (event: MouseEvent) => {
if (!props.disabled && !props.loading) {
emit('click', event);
}
};
</script>
<style lang="scss" scoped>
@import '../../styles/neumorphic-system.scss';
.professional-button {
display: inline-flex;
align-items: center;
justify-center: center;
gap: $space-2;
border: none;
border-radius: $radius-lg;
font-family: $font-body;
font-weight: $font-medium;
cursor: pointer;
transition: all $transition-base $ease-in-out-soft;
position: relative;
overflow: hidden;
// Size variants
&--sm {
padding: $space-2 $space-4;
font-size: $text-sm;
min-height: $button-height-sm;
&.professional-button--icon-only {
width: $button-height-sm;
padding: $space-2;
}
}
&--md {
padding: $space-3 $space-5;
font-size: $text-base;
min-height: $button-height-md;
&.professional-button--icon-only {
width: $button-height-md;
padding: $space-3;
}
}
&--lg {
padding: $space-4 $space-6;
font-size: $text-lg;
min-height: $button-height-lg;
&.professional-button--icon-only {
width: $button-height-lg;
padding: $space-4;
}
}
// Variant styles
&--primary {
background: linear-gradient(135deg, $primary-500, $primary-600);
color: white;
box-shadow: $shadow-soft-sm;
&:hover:not(.professional-button--disabled) {
background: linear-gradient(135deg, $primary-600, $primary-700);
box-shadow: $shadow-soft-md;
transform: translateY(-1px);
}
&:active:not(.professional-button--disabled) {
box-shadow: $shadow-inset-sm;
transform: translateY(0);
}
}
&--secondary {
background: linear-gradient(145deg, #ffffff, #f0f0f0);
color: $neutral-800;
box-shadow: $shadow-soft-sm;
&:hover:not(.professional-button--disabled) {
box-shadow: $shadow-soft-md;
transform: translateY(-1px);
}
&:active:not(.professional-button--disabled) {
box-shadow: $shadow-inset-sm;
transform: translateY(0);
}
}
&--outline {
background: transparent;
color: $primary-600;
border: 2px solid $primary-200;
box-shadow: none;
&:hover:not(.professional-button--disabled) {
background: rgba($primary-500, 0.05);
border-color: $primary-300;
}
&:active:not(.professional-button--disabled) {
background: rgba($primary-500, 0.1);
}
}
&--ghost {
background: transparent;
color: $neutral-700;
box-shadow: none;
&:hover:not(.professional-button--disabled) {
background: rgba($neutral-500, 0.08);
}
&:active:not(.professional-button--disabled) {
background: rgba($neutral-500, 0.12);
}
}
&--danger {
background: linear-gradient(135deg, $error-500, #dc2626);
color: white;
box-shadow: $shadow-soft-sm;
&:hover:not(.professional-button--disabled) {
background: linear-gradient(135deg, #dc2626, #b91c1c);
box-shadow: $shadow-soft-md;
transform: translateY(-1px);
}
&:active:not(.professional-button--disabled) {
box-shadow: $shadow-inset-sm;
transform: translateY(0);
}
}
// States
&--block {
width: 100%;
}
&--disabled {
opacity: 0.5;
cursor: not-allowed;
}
&--loading {
color: transparent;
}
// Icons
&__spinner {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 1.25rem;
height: 1.25rem;
color: inherit;
svg {
width: 100%;
height: 100%;
}
}
&__icon,
&__end-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 1.25em;
height: 1.25em;
flex-shrink: 0;
}
&__content {
flex: 1;
text-align: center;
}
// Focus state
&:focus-visible {
outline: none;
box-shadow: 0 0 0 3px rgba($primary-500, 0.2);
}
// Dark mode support
@include dark-mode {
&--secondary {
background: linear-gradient(145deg, $neutral-700, $neutral-800);
color: $neutral-100;
&:hover:not(.professional-button--disabled) {
background: linear-gradient(145deg, $neutral-600, $neutral-700);
}
}
&--ghost {
color: $neutral-300;
&:hover:not(.professional-button--disabled) {
background: rgba($neutral-400, 0.1);
}
}
&--outline {
color: $primary-400;
border-color: $primary-800;
&:hover:not(.professional-button--disabled) {
background: rgba($primary-400, 0.1);
border-color: $primary-700;
}
}
}
}
// Animation
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.animate-spin {
animation: spin 1s linear infinite;
}
</style>

View File

@ -1,320 +0,0 @@
<template>
<NeumorphicCard :elevation="elevation" :animated="animated" class="stat-card">
<div class="stat-card__content">
<div class="stat-card__header">
<div class="stat-card__icon-wrapper" :class="`stat-card__icon-wrapper--${variant}`">
<component :is="icon" v-if="icon" class="stat-card__icon" />
<div v-else class="stat-card__icon-placeholder">
<svg fill="currentColor" viewBox="0 0 20 20">
<path d="M2 11a1 1 0 011-1h2a1 1 0 011 1v5a1 1 0 01-1 1H3a1 1 0 01-1-1v-5zM8 7a1 1 0 011-1h2a1 1 0 011 1v9a1 1 0 01-1 1H9a1 1 0 01-1-1V7zM14 4a1 1 0 011-1h2a1 1 0 011 1v12a1 1 0 01-1 1h-2a1 1 0 01-1-1V4z" />
</svg>
</div>
</div>
<div class="stat-card__trend" v-if="trend">
<span :class="['stat-card__trend-badge', trendClass]">
<svg v-if="trend > 0" class="stat-card__trend-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" />
</svg>
<svg v-else class="stat-card__trend-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 17h8m0 0V9m0 8l-8-8-4 4-6-6" />
</svg>
{{ Math.abs(trend) }}%
</span>
</div>
</div>
<div class="stat-card__body">
<h3 class="stat-card__label">{{ label }}</h3>
<div class="stat-card__value-wrapper">
<span v-if="prefix" class="stat-card__prefix">{{ prefix }}</span>
<span class="stat-card__value">{{ formattedValue }}</span>
<span v-if="suffix" class="stat-card__suffix">{{ suffix }}</span>
</div>
<p v-if="description" class="stat-card__description">{{ description }}</p>
</div>
<div v-if="showProgress" class="stat-card__progress">
<div class="stat-card__progress-bar">
<div
class="stat-card__progress-fill"
:class="`stat-card__progress-fill--${variant}`"
:style="{ width: `${progress}%` }"
></div>
</div>
<span class="stat-card__progress-text">{{ progress }}% of target</span>
</div>
<div v-if="$slots.footer" class="stat-card__footer">
<slot name="footer"></slot>
</div>
</div>
</NeumorphicCard>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import type { Component } from 'vue';
import NeumorphicCard from './NeumorphicCard.vue';
interface Props {
label: string;
value: number | string;
trend?: number;
prefix?: string;
suffix?: string;
description?: string;
variant?: 'primary' | 'success' | 'warning' | 'danger' | 'info';
icon?: Component;
elevation?: 'none' | 'sm' | 'md' | 'lg' | 'xl';
animated?: boolean;
showProgress?: boolean;
progress?: number;
format?: 'number' | 'currency' | 'percentage';
}
const props = withDefaults(defineProps<Props>(), {
variant: 'primary',
elevation: 'md',
animated: true,
showProgress: false,
progress: 0,
format: 'number'
});
const formattedValue = computed(() => {
const val = props.value;
if (typeof val === 'string') return val;
switch (props.format) {
case 'currency':
return val.toLocaleString('en-US', { minimumFractionDigits: 0, maximumFractionDigits: 0 });
case 'percentage':
return val.toFixed(1);
default:
return val.toLocaleString('en-US');
}
});
const trendClass = computed(() => {
if (!props.trend) return '';
return props.trend > 0 ? 'stat-card__trend-badge--up' : 'stat-card__trend-badge--down';
});
</script>
<style lang="scss" scoped>
@import '../../styles/neumorphic-system.scss';
.stat-card {
height: 100%;
&__content {
display: flex;
flex-direction: column;
gap: $space-4;
}
&__header {
display: flex;
justify-content: space-between;
align-items: flex-start;
}
&__icon-wrapper {
width: 48px;
height: 48px;
border-radius: $radius-lg;
display: flex;
align-items: center;
justify-content: center;
box-shadow: $shadow-soft-sm;
transition: all $transition-base;
&--primary {
background: linear-gradient(135deg, rgba($primary-500, 0.1), rgba($primary-600, 0.15));
color: $primary-600;
}
&--success {
background: linear-gradient(135deg, rgba($success-500, 0.1), rgba($success-500, 0.15));
color: $success-500;
}
&--warning {
background: linear-gradient(135deg, rgba($warning-500, 0.1), rgba($warning-500, 0.15));
color: $warning-500;
}
&--danger {
background: linear-gradient(135deg, rgba($error-500, 0.1), rgba($error-500, 0.15));
color: $error-500;
}
&--info {
background: linear-gradient(135deg, rgba($info-500, 0.1), rgba($info-500, 0.15));
color: $info-500;
}
}
&__icon,
&__icon-placeholder {
width: 24px;
height: 24px;
}
&__trend {
display: flex;
align-items: center;
}
&__trend-badge {
display: inline-flex;
align-items: center;
gap: $space-1;
padding: $space-1 $space-2;
border-radius: $radius-full;
font-size: $text-sm;
font-weight: $font-semibold;
&--up {
background: linear-gradient(135deg, rgba($success-500, 0.1), rgba($success-500, 0.15));
color: $success-500;
}
&--down {
background: linear-gradient(135deg, rgba($error-500, 0.1), rgba($error-500, 0.15));
color: $error-500;
}
}
&__trend-icon {
width: 14px;
height: 14px;
}
&__body {
flex: 1;
}
&__label {
font-size: $text-sm;
font-weight: $font-medium;
color: $neutral-600;
margin-bottom: $space-2;
text-transform: uppercase;
letter-spacing: 0.025em;
}
&__value-wrapper {
display: flex;
align-items: baseline;
gap: $space-1;
margin-bottom: $space-2;
}
&__prefix,
&__suffix {
font-size: $text-lg;
color: $neutral-500;
}
&__value {
font-family: $font-heading;
font-size: $text-3xl;
font-weight: $font-bold;
color: $neutral-800;
line-height: 1;
}
&__description {
font-size: $text-sm;
color: $neutral-600;
line-height: $leading-normal;
}
&__progress {
margin-top: auto;
}
&__progress-bar {
width: 100%;
height: 8px;
background: rgba($neutral-300, 0.3);
border-radius: $radius-full;
overflow: hidden;
box-shadow: $shadow-inset-sm;
margin-bottom: $space-2;
}
&__progress-fill {
height: 100%;
border-radius: $radius-full;
transition: width $transition-slow $ease-out-soft;
&--primary {
background: linear-gradient(90deg, $primary-500, $primary-600);
}
&--success {
background: linear-gradient(90deg, $success-500, #059669);
}
&--warning {
background: linear-gradient(90deg, $warning-500, #D97706);
}
&--danger {
background: linear-gradient(90deg, $error-500, #DC2626);
}
&--info {
background: linear-gradient(90deg, $info-500, #2563EB);
}
}
&__progress-text {
font-size: $text-xs;
color: $neutral-500;
}
&__footer {
padding-top: $space-3;
border-top: 1px solid rgba($neutral-300, 0.3);
}
// Hover effect
&:hover {
.stat-card__icon-wrapper {
transform: scale(1.05);
box-shadow: $shadow-soft-md;
}
}
// Dark mode
@include dark-mode {
.stat-card__label {
color: $neutral-400;
}
.stat-card__value {
color: $neutral-100;
}
.stat-card__description {
color: $neutral-400;
}
.stat-card__prefix,
.stat-card__suffix {
color: $neutral-500;
}
.stat-card__progress-bar {
background: rgba($neutral-600, 0.3);
}
.stat-card__progress-text {
color: $neutral-500;
}
}
}
</style>

View File

@ -1,319 +0,0 @@
<template>
<div class="mockup-index">
<div class="container">
<header class="header">
<img src="/MONACOUSA-Flags_376x376.png" alt="MonacoUSA" class="logo" />
<h1 class="title">MonacoUSA Portal Design Mockups</h1>
<p class="subtitle">Professional Neumorphic Design System</p>
</header>
<section class="mockup-grid">
<NuxtLink to="/design-mockups/pages/auth/ProfessionalLogin" class="mockup-card">
<div class="card-icon card-icon--auth">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z" />
</svg>
</div>
<h2 class="card-title">Login Page</h2>
<p class="card-description">Professional split-screen login with branding section and neumorphic form</p>
<span class="card-link">View Mockup </span>
</NuxtLink>
<NuxtLink to="/design-mockups/pages/admin/ProfessionalAdminDashboard" class="mockup-card">
<div class="card-icon card-icon--admin">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
</div>
<h2 class="card-title">Admin Dashboard</h2>
<p class="card-description">Complete admin interface with sidebar navigation, stats, charts, and data tables</p>
<span class="card-link">View Mockup </span>
</NuxtLink>
<NuxtLink to="/design-mockups/pages/board/ProfessionalBoardDashboard" class="mockup-card">
<div class="card-icon card-icon--board">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
</svg>
</div>
<h2 class="card-title">Board Dashboard</h2>
<p class="card-description">Strategic insights, KPIs, governance features, and executive metrics</p>
<span class="card-link">View Mockup </span>
</NuxtLink>
<NuxtLink to="/design-mockups/pages/member/ProfessionalMemberDashboard" class="mockup-card">
<div class="card-icon card-icon--member">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />
</svg>
</div>
<h2 class="card-title">Member Dashboard</h2>
<p class="card-description">Member portal with events, resources, community features, and benefits</p>
<span class="card-link">View Mockup </span>
</NuxtLink>
</section>
<section class="components-section">
<h2 class="section-title">Core Components</h2>
<div class="component-list">
<div class="component-item">
<span class="component-name">NeumorphicCard</span>
<span class="component-status"> Complete</span>
</div>
<div class="component-item">
<span class="component-name">ProfessionalButton</span>
<span class="component-status"> Complete</span>
</div>
<div class="component-item">
<span class="component-name">StatCard</span>
<span class="component-status"> Complete</span>
</div>
</div>
</section>
<footer class="footer">
<p>Neumorphic Design System Professional & Inviting Built with Vue 3 & Nuxt 3</p>
</footer>
</div>
</div>
</template>
<script setup lang="ts">
// Disable authentication for mockups
definePageMeta({
auth: false,
layout: false
});
</script>
<style lang="scss" scoped>
// Import the neumorphic system
@import './styles/neumorphic-system.scss';
.mockup-index {
min-height: 100vh;
background: linear-gradient(135deg, $neutral-50 0%, $neutral-100 100%);
padding: $space-8 $space-6;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
.header {
text-align: center;
margin-bottom: $space-12;
.logo {
width: 80px;
height: 80px;
margin: 0 auto $space-4;
object-fit: contain;
}
.title {
font-family: $font-heading;
font-size: $text-4xl;
font-weight: $font-bold;
color: $neutral-800;
margin-bottom: $space-2;
}
.subtitle {
font-size: $text-lg;
color: $neutral-600;
}
}
.mockup-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: $space-6;
margin-bottom: $space-12;
}
.mockup-card {
display: block;
padding: $space-6;
background: linear-gradient(145deg, #ffffff, #f0f0f0);
border-radius: $radius-xl;
box-shadow: $shadow-soft-md;
text-decoration: none;
transition: all $transition-base;
&:hover {
transform: translateY(-4px);
box-shadow: $shadow-soft-lg;
.card-link {
color: $primary-700;
}
}
&:active {
transform: translateY(0);
box-shadow: $shadow-inset-sm;
}
.card-icon {
width: 60px;
height: 60px;
border-radius: $radius-lg;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: $space-4;
box-shadow: $shadow-soft-sm;
svg {
width: 28px;
height: 28px;
}
&--auth {
background: linear-gradient(135deg, rgba($primary-500, 0.1), rgba($primary-600, 0.15));
color: $primary-600;
}
&--admin {
background: linear-gradient(135deg, rgba($info-500, 0.1), rgba($info-500, 0.15));
color: $info-500;
}
&--board {
background: linear-gradient(135deg, rgba($success-500, 0.1), rgba($success-500, 0.15));
color: $success-500;
}
&--member {
background: linear-gradient(135deg, rgba($warning-500, 0.1), rgba($warning-500, 0.15));
color: $warning-500;
}
}
.card-title {
font-family: $font-heading;
font-size: $text-xl;
font-weight: $font-semibold;
color: $neutral-800;
margin-bottom: $space-2;
}
.card-description {
font-size: $text-sm;
color: $neutral-600;
line-height: $leading-relaxed;
margin-bottom: $space-4;
}
.card-link {
font-size: $text-sm;
font-weight: $font-medium;
color: $primary-600;
transition: color $transition-base;
}
}
.components-section {
background: linear-gradient(145deg, #ffffff, #f0f0f0);
border-radius: $radius-xl;
padding: $space-8;
box-shadow: $shadow-soft-md;
margin-bottom: $space-8;
.section-title {
font-family: $font-heading;
font-size: $text-2xl;
font-weight: $font-semibold;
color: $neutral-800;
margin-bottom: $space-6;
}
.component-list {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: $space-4;
}
.component-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: $space-3 $space-4;
background: rgba($neutral-100, 0.5);
border-radius: $radius-lg;
.component-name {
font-family: $font-mono;
font-size: $text-sm;
color: $neutral-700;
}
.component-status {
font-size: $text-sm;
color: $success-500;
}
}
}
.footer {
text-align: center;
padding: $space-6;
p {
font-size: $text-sm;
color: $neutral-600;
}
}
// Dark mode support
@include dark-mode {
.mockup-index {
background: linear-gradient(135deg, $neutral-900 0%, $neutral-800 100%);
}
.mockup-card {
background: linear-gradient(145deg, $neutral-800, $neutral-700);
.card-title {
color: $neutral-100;
}
.card-description {
color: $neutral-400;
}
}
.components-section {
background: linear-gradient(145deg, $neutral-800, $neutral-700);
.section-title {
color: $neutral-100;
}
.component-item {
background: rgba($neutral-700, 0.5);
.component-name {
color: $neutral-300;
}
}
}
.header {
.title {
color: $neutral-100;
}
.subtitle {
color: $neutral-400;
}
}
.footer p {
color: $neutral-400;
}
}
</style>

View File

@ -1,820 +0,0 @@
<template>
<div class="admin-dashboard">
<!-- Sidebar Navigation -->
<aside class="sidebar" :class="{ 'sidebar--collapsed': isSidebarCollapsed }">
<div class="sidebar-header">
<div class="sidebar-logo">
<img src="/MONACOUSA-Flags_376x376.png" alt="MonacoUSA" />
<span v-if="!isSidebarCollapsed" class="sidebar-title">Admin Portal</span>
</div>
<button @click="isSidebarCollapsed = !isSidebarCollapsed" class="sidebar-toggle">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</div>
<nav class="sidebar-nav">
<a
v-for="item in navItems"
:key="item.id"
:class="['sidebar-item', { 'sidebar-item--active': activeNav === item.id }]"
@click="activeNav = item.id"
>
<span class="sidebar-item-icon">
<component :is="item.icon" />
</span>
<span v-if="!isSidebarCollapsed" class="sidebar-item-label">{{ item.label }}</span>
<span v-if="!isSidebarCollapsed && item.badge" class="sidebar-item-badge">{{ item.badge }}</span>
</a>
</nav>
<div class="sidebar-footer">
<div class="sidebar-user">
<div class="sidebar-user-avatar">
<img src="https://via.placeholder.com/40" alt="Admin" />
</div>
<div v-if="!isSidebarCollapsed" class="sidebar-user-info">
<div class="sidebar-user-name">John Admin</div>
<div class="sidebar-user-role">System Admin</div>
</div>
</div>
</div>
</aside>
<!-- Main Content -->
<main class="main-content">
<!-- Top Bar -->
<header class="topbar">
<div class="topbar-left">
<h1 class="topbar-title">Dashboard Overview</h1>
<p class="topbar-subtitle">Welcome back, John. Here's what's happening today.</p>
</div>
<div class="topbar-right">
<button class="topbar-button">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
</button>
<button class="topbar-button topbar-button--notification">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
</svg>
<span class="notification-badge">3</span>
</button>
<div class="topbar-divider"></div>
<button class="topbar-user">
<img src="https://via.placeholder.com/32" alt="User" class="topbar-user-avatar" />
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
</button>
</div>
</header>
<!-- Stats Grid -->
<div class="stats-grid">
<StatCard
v-for="stat in stats"
:key="stat.title"
:title="stat.title"
:value="stat.value"
:change="stat.change"
:trend="stat.trend"
:icon="stat.icon"
/>
</div>
<!-- Content Grid -->
<div class="content-grid">
<!-- Chart Card -->
<NeumorphicCard class="chart-card">
<template #header>
<div class="card-header">
<h2 class="card-title">Revenue Overview</h2>
<div class="card-actions">
<button class="card-action">Day</button>
<button class="card-action card-action--active">Week</button>
<button class="card-action">Month</button>
<button class="card-action">Year</button>
</div>
</div>
</template>
<div class="chart-container">
<canvas ref="chartCanvas"></canvas>
</div>
</NeumorphicCard>
<!-- Activity Feed -->
<NeumorphicCard class="activity-card">
<template #header>
<div class="card-header">
<h2 class="card-title">Recent Activity</h2>
<button class="card-link">View All</button>
</div>
</template>
<div class="activity-list">
<div v-for="activity in activities" :key="activity.id" class="activity-item">
<div class="activity-icon" :class="`activity-icon--${activity.type}`">
<component :is="activity.icon" />
</div>
<div class="activity-content">
<p class="activity-description">{{ activity.description }}</p>
<span class="activity-time">{{ activity.time }}</span>
</div>
</div>
</div>
</NeumorphicCard>
<!-- Members Table -->
<NeumorphicCard class="table-card">
<template #header>
<div class="card-header">
<h2 class="card-title">Recent Members</h2>
<ProfessionalButton variant="outline" size="sm">
View All Members
</ProfessionalButton>
</div>
</template>
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th>Member</th>
<th>Status</th>
<th>Joined</th>
<th>Last Active</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr v-for="member in recentMembers" :key="member.id">
<td>
<div class="member-cell">
<img :src="member.avatar" :alt="member.name" class="member-avatar" />
<div>
<div class="member-name">{{ member.name }}</div>
<div class="member-email">{{ member.email }}</div>
</div>
</div>
</td>
<td>
<span class="status-badge" :class="`status-badge--${member.status}`">
{{ member.status }}
</span>
</td>
<td>{{ member.joined }}</td>
<td>{{ member.lastActive }}</td>
<td>
<button class="table-action">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 5v.01M12 12v.01M12 19v.01M12 6a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2z" />
</svg>
</button>
</td>
</tr>
</tbody>
</table>
</div>
</NeumorphicCard>
</div>
</main>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import NeumorphicCard from '../../components/core/NeumorphicCard.vue';
import ProfessionalButton from '../../components/core/ProfessionalButton.vue';
import StatCard from '../../components/core/StatCard.vue';
import Chart from 'chart.js/auto';
// Disable authentication for mockup
definePageMeta({
auth: false,
layout: false
});
// Icons (simplified for demo)
const HomeIcon = { template: '<svg fill="currentColor" viewBox="0 0 20 20"><path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"/></svg>' };
const UsersIcon = { template: '<svg fill="currentColor" viewBox="0 0 20 20"><path d="M9 6a3 3 0 11-6 0 3 3 0 016 0zM17 6a3 3 0 11-6 0 3 3 0 016 0zM12.93 17c.046-.327.07-.66.07-1a6.97 6.97 0 00-1.5-4.33A5 5 0 0119 16v1h-6.07zM6 11a5 5 0 015 5v1H1v-1a5 5 0 015-5z"/></svg>' };
const CalendarIcon = { template: '<svg fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z" clip-rule="evenodd"/></svg>' };
const ChartIcon = { template: '<svg fill="currentColor" viewBox="0 0 20 20"><path d="M2 11a1 1 0 011-1h2a1 1 0 011 1v5a1 1 0 01-1 1H3a1 1 0 01-1-1v-5zM8 7a1 1 0 011-1h2a1 1 0 011 1v9a1 1 0 01-1 1H9a1 1 0 01-1-1V7zM14 4a1 1 0 011-1h2a1 1 0 011 1v12a1 1 0 01-1 1h-2a1 1 0 01-1-1V4z"/></svg>' };
// Data
const isSidebarCollapsed = ref(false);
const activeNav = ref('dashboard');
const chartCanvas = ref<HTMLCanvasElement | null>(null);
const navItems = [
{ id: 'dashboard', label: 'Dashboard', icon: HomeIcon },
{ id: 'members', label: 'Members', icon: UsersIcon, badge: '127' },
{ id: 'events', label: 'Events', icon: CalendarIcon, badge: '5' },
{ id: 'analytics', label: 'Analytics', icon: ChartIcon },
];
const stats = [
{ title: 'Total Members', value: '1,247', change: '+12%', trend: 'up', icon: UsersIcon },
{ title: 'Active Events', value: '18', change: '+3', trend: 'up', icon: CalendarIcon },
{ title: 'Monthly Revenue', value: '$48,392', change: '+8%', trend: 'up', icon: ChartIcon },
{ title: 'Engagement Rate', value: '87%', change: '-2%', trend: 'down', icon: ChartIcon },
];
const activities = [
{ id: 1, type: 'user', icon: UsersIcon, description: 'New member registration: Sarah Johnson', time: '5 minutes ago' },
{ id: 2, type: 'event', icon: CalendarIcon, description: 'Annual Gala event updated', time: '1 hour ago' },
{ id: 3, type: 'payment', icon: ChartIcon, description: 'Payment received from Michael Brown', time: '2 hours ago' },
{ id: 4, type: 'user', icon: UsersIcon, description: 'Member profile updated: Robert Davis', time: '3 hours ago' },
];
const recentMembers = [
{ id: 1, name: 'Sarah Johnson', email: 'sarah@example.com', avatar: 'https://via.placeholder.com/40', status: 'active', joined: 'Jan 15, 2024', lastActive: '2 hours ago' },
{ id: 2, name: 'Michael Brown', email: 'michael@example.com', avatar: 'https://via.placeholder.com/40', status: 'active', joined: 'Jan 10, 2024', lastActive: '1 day ago' },
{ id: 3, name: 'Emma Wilson', email: 'emma@example.com', avatar: 'https://via.placeholder.com/40', status: 'pending', joined: 'Jan 8, 2024', lastActive: '3 days ago' },
{ id: 4, name: 'James Taylor', email: 'james@example.com', avatar: 'https://via.placeholder.com/40', status: 'inactive', joined: 'Dec 20, 2023', lastActive: '1 week ago' },
];
// Initialize chart
onMounted(() => {
if (chartCanvas.value) {
new Chart(chartCanvas.value, {
type: 'line',
data: {
labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
datasets: [{
label: 'Revenue',
data: [12000, 19000, 15000, 25000, 22000, 30000, 28000],
borderColor: '#DC2626',
backgroundColor: 'rgba(220, 38, 38, 0.1)',
tension: 0.4,
fill: true
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
}
},
scales: {
y: {
beginAtZero: true,
grid: {
display: true,
color: 'rgba(0, 0, 0, 0.05)'
}
},
x: {
grid: {
display: false
}
}
}
}
});
}
});
</script>
<style lang="scss" scoped>
@import '../../styles/neumorphic-system.scss';
.admin-dashboard {
display: flex;
min-height: 100vh;
background: $neutral-100;
}
// Sidebar
.sidebar {
width: $sidebar-width;
background: white;
box-shadow: $shadow-soft-md;
display: flex;
flex-direction: column;
transition: width $transition-base;
&--collapsed {
width: $sidebar-width-collapsed;
}
&-header {
padding: $space-6;
border-bottom: 1px solid $neutral-200;
display: flex;
align-items: center;
justify-content: space-between;
}
&-logo {
display: flex;
align-items: center;
gap: $space-3;
img {
width: 40px;
height: 40px;
border-radius: $radius-lg;
}
}
&-title {
font-weight: $font-semibold;
color: $neutral-800;
white-space: nowrap;
}
&-toggle {
background: none;
border: none;
padding: $space-2;
cursor: pointer;
color: $neutral-600;
border-radius: $radius-md;
transition: all $transition-base;
svg {
width: 20px;
height: 20px;
}
&:hover {
background: $neutral-100;
}
}
&-nav {
flex: 1;
padding: $space-4;
}
&-item {
display: flex;
align-items: center;
gap: $space-3;
padding: $space-3 $space-4;
margin-bottom: $space-2;
border-radius: $radius-lg;
color: $neutral-600;
text-decoration: none;
cursor: pointer;
transition: all $transition-base;
position: relative;
&:hover {
background: $neutral-100;
color: $neutral-800;
}
&--active {
background: linear-gradient(135deg, $primary-500, $primary-600);
color: white;
box-shadow: $shadow-soft-sm;
}
&-icon {
width: 20px;
height: 20px;
flex-shrink: 0;
svg {
width: 100%;
height: 100%;
}
}
&-label {
font-size: $text-sm;
font-weight: $font-medium;
white-space: nowrap;
}
&-badge {
margin-left: auto;
padding: 2px 8px;
background: $primary-100;
color: $primary-700;
border-radius: $radius-full;
font-size: $text-xs;
font-weight: $font-semibold;
}
}
&-footer {
padding: $space-4;
border-top: 1px solid $neutral-200;
}
&-user {
display: flex;
align-items: center;
gap: $space-3;
padding: $space-3;
&-avatar {
width: 40px;
height: 40px;
border-radius: $radius-full;
overflow: hidden;
flex-shrink: 0;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
&-info {
flex: 1;
min-width: 0;
}
&-name {
font-size: $text-sm;
font-weight: $font-semibold;
color: $neutral-800;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
&-role {
font-size: $text-xs;
color: $neutral-500;
}
}
}
// Main Content
.main-content {
flex: 1;
display: flex;
flex-direction: column;
overflow-x: hidden;
}
// Topbar
.topbar {
background: white;
padding: $space-6;
box-shadow: $shadow-soft-sm;
display: flex;
align-items: center;
justify-content: space-between;
&-left {
flex: 1;
}
&-title {
font-size: $text-2xl;
font-weight: $font-bold;
color: $neutral-800;
margin-bottom: $space-1;
}
&-subtitle {
font-size: $text-sm;
color: $neutral-600;
}
&-right {
display: flex;
align-items: center;
gap: $space-3;
}
&-button {
position: relative;
padding: $space-2;
background: white;
border: none;
border-radius: $radius-lg;
cursor: pointer;
color: $neutral-600;
box-shadow: $shadow-soft-sm;
transition: all $transition-base;
svg {
width: 20px;
height: 20px;
}
&:hover {
box-shadow: $shadow-soft-md;
color: $neutral-800;
}
&--notification {
.notification-badge {
position: absolute;
top: -4px;
right: -4px;
width: 18px;
height: 18px;
background: $error-500;
color: white;
border-radius: $radius-full;
font-size: $text-xs;
display: flex;
align-items: center;
justify-content: center;
font-weight: $font-bold;
}
}
}
&-divider {
width: 1px;
height: 24px;
background: $neutral-200;
}
&-user {
display: flex;
align-items: center;
gap: $space-2;
padding: $space-2;
background: white;
border: none;
border-radius: $radius-lg;
cursor: pointer;
box-shadow: $shadow-soft-sm;
transition: all $transition-base;
&:hover {
box-shadow: $shadow-soft-md;
}
&-avatar {
width: 32px;
height: 32px;
border-radius: $radius-full;
}
}
}
// Stats Grid
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: $space-6;
padding: $space-6;
}
// Content Grid
.content-grid {
display: grid;
grid-template-columns: 2fr 1fr;
gap: $space-6;
padding: 0 $space-6 $space-6;
@media (max-width: $breakpoint-lg) {
grid-template-columns: 1fr;
}
.chart-card {
grid-column: 1;
grid-row: 1;
}
.activity-card {
grid-column: 2;
grid-row: 1;
}
.table-card {
grid-column: 1 / -1;
}
}
// Card styles
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
}
.card-title {
font-size: $text-lg;
font-weight: $font-semibold;
color: $neutral-800;
}
.card-actions {
display: flex;
gap: $space-2;
}
.card-action {
padding: $space-2 $space-3;
background: transparent;
border: none;
border-radius: $radius-md;
font-size: $text-sm;
color: $neutral-600;
cursor: pointer;
transition: all $transition-base;
&:hover {
background: $neutral-100;
color: $neutral-800;
}
&--active {
background: $primary-500;
color: white;
box-shadow: $shadow-soft-sm;
}
}
.card-link {
background: none;
border: none;
color: $primary-600;
font-size: $text-sm;
font-weight: $font-medium;
cursor: pointer;
&:hover {
color: $primary-700;
text-decoration: underline;
}
}
// Chart
.chart-container {
height: 300px;
padding: $space-4 0;
}
// Activity List
.activity-list {
display: flex;
flex-direction: column;
gap: $space-4;
}
.activity-item {
display: flex;
gap: $space-3;
&-icon {
width: 40px;
height: 40px;
border-radius: $radius-lg;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
svg {
width: 20px;
height: 20px;
}
&--user {
background: $info-100;
color: $info-500;
}
&--event {
background: $warning-100;
color: $warning-500;
}
&--payment {
background: $success-100;
color: $success-500;
}
}
&-content {
flex: 1;
min-width: 0;
}
&-description {
font-size: $text-sm;
color: $neutral-700;
margin-bottom: $space-1;
}
&-time {
font-size: $text-xs;
color: $neutral-500;
}
}
// Table
.table-wrapper {
overflow-x: auto;
}
.data-table {
width: 100%;
thead {
tr {
border-bottom: 2px solid $neutral-200;
}
th {
text-align: left;
padding: $space-3;
font-size: $text-xs;
font-weight: $font-semibold;
text-transform: uppercase;
letter-spacing: 0.05em;
color: $neutral-600;
}
}
tbody {
tr {
border-bottom: 1px solid $neutral-100;
transition: background $transition-base;
&:hover {
background: $neutral-50;
}
}
td {
padding: $space-3;
font-size: $text-sm;
color: $neutral-700;
}
}
}
.member-cell {
display: flex;
align-items: center;
gap: $space-3;
}
.member-avatar {
width: 40px;
height: 40px;
border-radius: $radius-full;
}
.member-name {
font-weight: $font-medium;
color: $neutral-800;
margin-bottom: 2px;
}
.member-email {
font-size: $text-xs;
color: $neutral-500;
}
.status-badge {
display: inline-block;
padding: 4px 12px;
border-radius: $radius-full;
font-size: $text-xs;
font-weight: $font-medium;
&--active {
background: $success-100;
color: $success-500;
}
&--pending {
background: $warning-100;
color: $warning-500;
}
&--inactive {
background: $neutral-100;
color: $neutral-500;
}
}
.table-action {
padding: $space-2;
background: none;
border: none;
cursor: pointer;
color: $neutral-500;
border-radius: $radius-md;
transition: all $transition-base;
&:hover {
background: $neutral-100;
color: $neutral-700;
}
}</style>

View File

@ -1,425 +0,0 @@
<template>
<div class="login-page">
<div class="login-container">
<!-- Left Side - Branding -->
<div class="login-branding">
<div class="branding-content">
<div class="logo-container">
<img src="/MONACOUSA-Flags_376x376.png" alt="MonacoUSA" class="logo" />
</div>
<h1 class="brand-title">MonacoUSA Portal</h1>
<p class="brand-tagline">Excellence in Partnership</p>
<div class="feature-list">
<div class="feature-item" v-for="feature in features" :key="feature">
<svg class="feature-icon" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
</svg>
<span>{{ feature }}</span>
</div>
</div>
</div>
<!-- Background pattern -->
<div class="branding-pattern"></div>
</div>
<!-- Right Side - Login Form -->
<div class="login-form-section">
<NeumorphicCard size="lg" elevation="lg" class="login-card">
<template #header>
<h2 class="login-title">Welcome Back</h2>
<p class="login-subtitle">Sign in to access your account</p>
</template>
<form @submit.prevent="handleLogin" class="login-form">
<div class="form-group">
<label for="email" class="form-label">Email Address</label>
<div class="input-wrapper">
<input
id="email"
v-model="credentials.email"
type="email"
class="form-input"
placeholder="you@example.com"
required
/>
<span class="input-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
</span>
</div>
</div>
<div class="form-group">
<label for="password" class="form-label">Password</label>
<div class="input-wrapper">
<input
id="password"
v-model="credentials.password"
:type="showPassword ? 'text' : 'password'"
class="form-input"
placeholder="Enter your password"
required
/>
<button
type="button"
@click="showPassword = !showPassword"
class="input-icon clickable"
>
<svg v-if="!showPassword" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
<svg v-else fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21" />
</svg>
</button>
</div>
</div>
<div class="form-options">
<label class="checkbox-label">
<input v-model="credentials.rememberMe" type="checkbox" class="checkbox-input" />
<span>Remember me</span>
</label>
<button type="button" class="forgot-password-link" @click="showForgotPassword = true">
Forgot password?
</button>
</div>
<ProfessionalButton
type="submit"
variant="primary"
size="lg"
block
:loading="loading"
class="login-button"
>
Sign In
</ProfessionalButton>
</form>
<template #footer>
<div class="login-footer">
<p class="signup-prompt">
Don't have an account?
<a href="/signup" class="signup-link">Create Account</a>
</p>
</div>
</template>
</NeumorphicCard>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import NeumorphicCard from '../../components/core/NeumorphicCard.vue';
import ProfessionalButton from '../../components/core/ProfessionalButton.vue';
// Disable authentication for mockup
definePageMeta({
auth: false,
layout: false
});
// Data
const credentials = ref({
email: '',
password: '',
rememberMe: false
});
const showPassword = ref(false);
const loading = ref(false);
const showForgotPassword = ref(false);
const features = [
'Secure Member Access',
'Event Management',
'Document Library',
'Payment Processing'
];
// Methods
const handleLogin = async () => {
loading.value = true;
// Simulate API call
setTimeout(() => {
loading.value = false;
console.log('Login with:', credentials.value);
}, 2000);
};
</script>
<style lang="scss" scoped>
@import '../../styles/neumorphic-system.scss';
.login-page {
min-height: 100vh;
background: linear-gradient(135deg, $neutral-50 0%, $neutral-100 100%);
display: flex;
align-items: center;
justify-content: center;
}
.login-container {
width: 100%;
max-width: 1400px;
margin: 0 auto;
display: grid;
grid-template-columns: 1fr 1fr;
min-height: 100vh;
@media (max-width: $breakpoint-md) {
grid-template-columns: 1fr;
}
}
// Left side - Branding
.login-branding {
position: relative;
background: linear-gradient(135deg, $primary-600, $primary-800);
display: flex;
align-items: center;
justify-content: center;
padding: $space-12;
overflow: hidden;
@media (max-width: $breakpoint-md) {
display: none;
}
.branding-content {
position: relative;
z-index: 2;
text-align: center;
color: white;
}
.logo-container {
width: 120px;
height: 120px;
margin: 0 auto $space-6;
background: white;
border-radius: $radius-2xl;
padding: $space-4;
box-shadow: $shadow-soft-lg;
.logo {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.brand-title {
font-family: $font-heading;
font-size: $text-3xl;
font-weight: $font-bold;
margin-bottom: $space-2;
letter-spacing: -0.025em;
}
.brand-tagline {
font-size: $text-lg;
opacity: 0.9;
margin-bottom: $space-12;
}
.feature-list {
text-align: left;
max-width: 300px;
margin: 0 auto;
}
.feature-item {
display: flex;
align-items: center;
gap: $space-3;
margin-bottom: $space-4;
font-size: $text-base;
.feature-icon {
width: 20px;
height: 20px;
flex-shrink: 0;
color: rgba(white, 0.9);
}
}
.branding-pattern {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-image: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.05'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
opacity: 0.5;
}
}
// Right side - Login form
.login-form-section {
display: flex;
align-items: center;
justify-content: center;
padding: $space-8;
.login-card {
width: 100%;
max-width: 480px;
}
.login-title {
font-family: $font-heading;
font-size: $text-2xl;
font-weight: $font-bold;
color: $neutral-800;
margin-bottom: $space-2;
}
.login-subtitle {
font-size: $text-base;
color: $neutral-600;
}
.login-form {
.form-group {
margin-bottom: $space-5;
}
.form-label {
display: block;
font-size: $text-sm;
font-weight: $font-medium;
color: $neutral-700;
margin-bottom: $space-2;
}
.input-wrapper {
position: relative;
}
.form-input {
width: 100%;
padding: $space-3 $space-4;
padding-right: $space-12;
background: $neutral-50;
border: none;
border-radius: $radius-lg;
box-shadow: $shadow-inset-sm;
font-size: $text-base;
color: $neutral-800;
transition: all $transition-base;
&::placeholder {
color: $neutral-400;
}
&:focus {
outline: none;
box-shadow: $shadow-inset-md, 0 0 0 3px rgba($primary-500, 0.1);
}
}
.input-icon {
position: absolute;
right: $space-3;
top: 50%;
transform: translateY(-50%);
width: 20px;
height: 20px;
color: $neutral-400;
&.clickable {
cursor: pointer;
background: none;
border: none;
padding: 0;
transition: color $transition-base;
&:hover {
color: $neutral-600;
}
}
}
.form-options {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: $space-6;
}
.checkbox-label {
display: flex;
align-items: center;
gap: $space-2;
font-size: $text-sm;
color: $neutral-700;
cursor: pointer;
.checkbox-input {
width: 18px;
height: 18px;
border-radius: $radius-sm;
cursor: pointer;
}
}
.forgot-password-link {
background: none;
border: none;
color: $primary-600;
font-size: $text-sm;
font-weight: $font-medium;
cursor: pointer;
padding: 0;
transition: color $transition-base;
&:hover {
color: $primary-700;
text-decoration: underline;
}
}
.login-button {
margin-top: $space-4;
}
}
.login-footer {
text-align: center;
.signup-prompt {
font-size: $text-sm;
color: $neutral-600;
}
.signup-link {
color: $primary-600;
font-weight: $font-medium;
text-decoration: none;
margin-left: $space-1;
transition: color $transition-base;
&:hover {
color: $primary-700;
text-decoration: underline;
}
}
}
}
// Dark mode support
@include dark-mode {
.login-page {
background: linear-gradient(135deg, $neutral-900 0%, $neutral-800 100%);
}
}
</style>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,361 +0,0 @@
// MonacoUSA Portal Design System
// Professional Neumorphic Design with Enhanced Components
// ========================================
// 1. COLOR PALETTE
// ========================================
// Brand Colors
$monaco-red: #CC0000;
$monaco-dark-red: #990000;
$monaco-light-red: #FF3333;
// Primary Colors (Monaco Red)
$primary-50: #FEF2F2;
$primary-100: #FEE2E2;
$primary-200: #FECACA;
$primary-300: #FCA5A5;
$primary-400: #F87171;
$primary-500: #EF4444;
$primary-600: $monaco-red;
$primary-700: $monaco-dark-red;
$primary-800: #660000;
$primary-900: #450A0A;
// Neutral Colors
$neutral-50: #F9FAFB;
$neutral-100: #F3F4F6;
$neutral-200: #E5E7EB;
$neutral-300: #D1D5DB;
$neutral-400: #9CA3AF;
$neutral-500: #6B7280;
$neutral-600: #4B5563;
$neutral-700: #374151;
$neutral-800: #1F2937;
$neutral-900: #111827;
// Blue Colors (for dropdowns and accents)
$blue-50: #EFF6FF;
$blue-100: #DBEAFE;
$blue-200: #BFDBFE;
$blue-300: #93C5FD;
$blue-400: #60A5FA;
$blue-500: #3B82F6;
$blue-600: #2563EB;
$blue-700: #1D4ED8;
$blue-800: #1E40AF;
$blue-900: #1E3A8A;
// Semantic Colors
$success-500: #10B981;
$warning-500: #F59E0B;
$error-500: #EF4444;
$info-500: #3B82F6;
// ========================================
// 2. TYPOGRAPHY
// ========================================
$font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
$font-heading: 'Inter', $font-sans;
$font-mono: 'Fira Code', 'Courier New', monospace;
// Font Sizes
$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
// Font Weights
$font-light: 300;
$font-normal: 400;
$font-medium: 500;
$font-semibold: 600;
$font-bold: 700;
// Line Heights
$leading-tight: 1.25;
$leading-normal: 1.5;
$leading-relaxed: 1.625;
// ========================================
// 3. SPACING SYSTEM
// ========================================
$space-0: 0;
$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
// ========================================
// 4. NEUMORPHIC SHADOWS
// ========================================
// Soft Shadows (light theme)
$shadow-soft-xs:
2px 2px 4px rgba(0, 0, 0, 0.07),
-2px -2px 4px rgba(255, 255, 255, 0.95);
$shadow-soft-sm:
4px 4px 8px rgba(0, 0, 0, 0.08),
-4px -4px 8px rgba(255, 255, 255, 0.95);
$shadow-soft-md:
8px 8px 16px rgba(0, 0, 0, 0.1),
-8px -8px 16px rgba(255, 255, 255, 0.95);
$shadow-soft-lg:
12px 12px 24px rgba(0, 0, 0, 0.12),
-12px -12px 24px rgba(255, 255, 255, 0.95);
$shadow-soft-xl:
16px 16px 32px rgba(0, 0, 0, 0.14),
-16px -16px 32px rgba(255, 255, 255, 0.95);
// Inset Shadows (for pressed/input states)
$shadow-inset-sm:
inset 4px 4px 8px rgba(0, 0, 0, 0.1),
inset -4px -4px 8px rgba(255, 255, 255, 0.95);
$shadow-inset-md:
inset 6px 6px 12px rgba(0, 0, 0, 0.15),
inset -6px -6px 12px rgba(255, 255, 255, 0.95);
// Glassmorphic Shadow
$shadow-glass:
0 8px 32px rgba(0, 0, 0, 0.1),
0 0 0 1px rgba(255, 255, 255, 0.3);
// Morphing Dropdown Shadow
$shadow-morphing:
0 10px 40px rgba(0, 0, 0, 0.12),
0 2px 10px rgba(0, 0, 0, 0.08);
// ========================================
// 5. BORDER RADIUS
// ========================================
$radius-sm: 0.375rem; // 6px
$radius-md: 0.5rem; // 8px
$radius-lg: 0.75rem; // 12px
$radius-xl: 1rem; // 16px
$radius-2xl: 1.5rem; // 24px
$radius-3xl: 2rem; // 32px
$radius-full: 9999px;
// ========================================
// 6. TRANSITIONS
// ========================================
$transition-base: 0.3s ease;
$transition-fast: 0.15s ease;
$transition-slow: 0.5s ease;
// Spring Animation (for dropdowns)
$spring-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
$spring-smooth: cubic-bezier(0.4, 0, 0.2, 1);
// ========================================
// 7. BREAKPOINTS
// ========================================
$breakpoint-sm: 640px;
$breakpoint-md: 768px;
$breakpoint-lg: 1024px;
$breakpoint-xl: 1280px;
$breakpoint-2xl: 1536px;
// ========================================
// 8. Z-INDEX LAYERS
// ========================================
$z-base: 0;
$z-dropdown: 10;
$z-sticky: 20;
$z-overlay: 30;
$z-modal: 40;
$z-popover: 50;
$z-tooltip: 60;
$z-notification: 70;
// ========================================
// 9. GLASSMORPHISM MIXINS
// ========================================
@mixin glassmorphism($bg-opacity: 0.8, $blur: 12px) {
background: rgba(255, 255, 255, $bg-opacity);
backdrop-filter: blur($blur);
-webkit-backdrop-filter: blur($blur);
border: 1px solid rgba(255, 255, 255, 0.3);
}
@mixin glassmorphism-blue($bg-opacity: 0.3, $blur: 12px) {
background: rgba($blue-50, $bg-opacity);
backdrop-filter: blur($blur);
-webkit-backdrop-filter: blur($blur);
border: 1px solid rgba($blue-200, 0.3);
}
// ========================================
// 10. COMPONENT MIXINS
// ========================================
// Neumorphic Card
@mixin neumorphic-card($elevation: 'md') {
background: linear-gradient(145deg, #ffffff, #f0f0f0);
border-radius: $radius-xl;
@if $elevation == 'sm' {
box-shadow: $shadow-soft-sm;
} @else if $elevation == 'md' {
box-shadow: $shadow-soft-md;
} @else if $elevation == 'lg' {
box-shadow: $shadow-soft-lg;
} @else if $elevation == 'xl' {
box-shadow: $shadow-soft-xl;
}
}
// Neumorphic Button
@mixin neumorphic-button() {
background: linear-gradient(145deg, #ffffff, #f0f0f0);
box-shadow: $shadow-soft-sm;
transition: all $transition-base;
&:hover {
box-shadow: $shadow-soft-md;
transform: translateY(-2px);
}
&:active {
box-shadow: $shadow-inset-sm;
transform: translateY(0);
}
}
// Morphing Dropdown
@mixin morphing-dropdown() {
@include glassmorphism-blue(0.8, 16px);
box-shadow: $shadow-morphing;
border-radius: $radius-lg;
overflow: hidden;
animation: morphIn 0.3s $spring-smooth;
}
// Focus Ring
@mixin focus-ring($color: $primary-500) {
&:focus {
outline: none;
box-shadow:
0 0 0 3px rgba($color, 0.1),
$shadow-soft-md;
border-color: rgba($color, 0.5);
}
}
// ========================================
// 11. ANIMATIONS
// ========================================
@keyframes morphIn {
from {
opacity: 0;
transform: scale(0.95) translateY(-10px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
// ========================================
// 12. UTILITY CLASSES
// ========================================
.backdrop-blur {
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
}
.glass-effect {
@include glassmorphism();
}
.glass-blue {
@include glassmorphism-blue();
}
.shadow-neumorphic {
box-shadow: $shadow-soft-md;
}
.text-gradient {
background: linear-gradient(135deg, $primary-600, $primary-800);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
// ========================================
// 13. RESPONSIVE UTILITIES
// ========================================
@mixin responsive($breakpoint) {
@media (min-width: $breakpoint) {
@content;
}
}
// Usage: @include responsive($breakpoint-md) { ... }
// ========================================
// 14. DARK MODE SUPPORT
// ========================================
@mixin dark-mode {
@media (prefers-color-scheme: dark) {
@content;
}
[data-theme="dark"] & {
@content;
}
}

View File

@ -1,320 +0,0 @@
// MonacoUSA Portal - Neumorphic Design System
// Professional, Modern, and Inviting Design Language
// ============================================
// 1. Color Palette
// ============================================
// Primary - Monaco Red (Sophisticated)
$primary-50: #FEF2F2;
$primary-100: #FEE2E2;
$primary-200: #FECACA;
$primary-300: #FCA5A5;
$primary-400: #F87171;
$primary-500: #DC2626; // Main brand
$primary-600: #B91C1C; // Hover states
$primary-700: #991B1B;
$primary-800: #7F1D1D;
$primary-900: #450A0A;
// Neutral Grays (Professional Workspace)
$neutral-50: #FAFAFA;
$neutral-100: #F5F5F5;
$neutral-200: #E5E5E5;
$neutral-300: #D4D4D4;
$neutral-400: #A3A3A3;
$neutral-500: #737373;
$neutral-600: #525252;
$neutral-700: #404040;
$neutral-800: #262626;
$neutral-900: #171717;
// Semantic Colors
$success-500: #10B981;
$success-100: #D1FAE5;
$warning-500: #F59E0B;
$warning-100: #FEF3C7;
$error-500: #EF4444;
$error-100: #FEE2E2;
$info-500: #3B82F6;
$info-100: #DBEAFE;
// ============================================
// 2. Neumorphic Shadow System
// ============================================
// Light Mode Shadows
$shadow-soft-xs: 2px 2px 4px rgba(0, 0, 0, 0.05), -2px -2px 4px rgba(255, 255, 255, 0.8);
$shadow-soft-sm: 4px 4px 8px rgba(0, 0, 0, 0.08), -4px -4px 8px rgba(255, 255, 255, 0.9);
$shadow-soft-md: 8px 8px 16px rgba(0, 0, 0, 0.1), -8px -8px 16px rgba(255, 255, 255, 0.95);
$shadow-soft-lg: 15px 15px 30px rgba(0, 0, 0, 0.12), -15px -15px 30px rgba(255, 255, 255, 1);
$shadow-soft-xl: 20px 20px 40px rgba(0, 0, 0, 0.15), -20px -20px 40px rgba(255, 255, 255, 1);
// Inset Shadows (for pressed states)
$shadow-inset-sm: inset 2px 2px 4px rgba(0, 0, 0, 0.08), inset -2px -2px 4px rgba(255, 255, 255, 0.9);
$shadow-inset-md: inset 4px 4px 8px rgba(0, 0, 0, 0.1), inset -4px -4px 8px rgba(255, 255, 255, 0.95);
$shadow-inset-lg: inset 8px 8px 16px rgba(0, 0, 0, 0.12), inset -8px -8px 16px rgba(255, 255, 255, 1);
// Dark Mode Shadows
$shadow-dark-soft-sm: 4px 4px 8px rgba(0, 0, 0, 0.3), -4px -4px 8px rgba(255, 255, 255, 0.02);
$shadow-dark-soft-md: 8px 8px 16px rgba(0, 0, 0, 0.4), -8px -8px 16px rgba(255, 255, 255, 0.03);
$shadow-dark-soft-lg: 15px 15px 30px rgba(0, 0, 0, 0.5), -15px -15px 30px rgba(255, 255, 255, 0.04);
$shadow-dark-inset-md: inset 4px 4px 8px rgba(0, 0, 0, 0.4), inset -4px -4px 8px rgba(255, 255, 255, 0.03);
// ============================================
// 3. Typography System
// ============================================
$font-heading: 'Inter', 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
$font-body: 'Inter', 'SF Pro Text', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
$font-mono: 'JetBrains Mono', 'Monaco', 'Courier New', monospace;
// Type Scale (Major Third - 1.25)
$text-xs: 0.75rem; // 12px
$text-sm: 0.875rem; // 14px
$text-base: 1rem; // 16px
$text-lg: 1.25rem; // 20px
$text-xl: 1.563rem; // 25px
$text-2xl: 1.953rem; // 31px
$text-3xl: 2.441rem; // 39px
$text-4xl: 3.052rem; // 49px
// Font Weights
$font-light: 300;
$font-regular: 400;
$font-medium: 500;
$font-semibold: 600;
$font-bold: 700;
// Line Heights
$leading-tight: 1.25;
$leading-normal: 1.5;
$leading-relaxed: 1.75;
// ============================================
// 4. Spacing System
// ============================================
$space-0: 0;
$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
$space-24: 6rem; // 96px
// ============================================
// 5. Border Radius
// ============================================
$radius-sm: 0.375rem; // 6px
$radius-md: 0.5rem; // 8px
$radius-lg: 0.75rem; // 12px
$radius-xl: 1rem; // 16px
$radius-2xl: 1.5rem; // 24px
$radius-3xl: 2rem; // 32px
$radius-full: 9999px; // Full round
// ============================================
// 6. Transitions
// ============================================
$transition-fast: 150ms ease-in-out;
$transition-base: 250ms ease-in-out;
$transition-slow: 350ms ease-in-out;
$transition-slower: 500ms ease-in-out;
// Easing Functions
$ease-in-out-soft: cubic-bezier(0.4, 0, 0.2, 1);
$ease-out-soft: cubic-bezier(0, 0, 0.2, 1);
$ease-in-soft: cubic-bezier(0.4, 0, 1, 1);
// ============================================
// 7. Breakpoints
// ============================================
$breakpoint-xs: 475px;
$breakpoint-sm: 640px;
$breakpoint-md: 768px;
$breakpoint-lg: 1024px;
$breakpoint-xl: 1280px;
$breakpoint-2xl: 1536px;
// ============================================
// 8. Z-Index Scale
// ============================================
$z-base: 0;
$z-dropdown: 1000;
$z-sticky: 1020;
$z-fixed: 1030;
$z-modal-backdrop: 1040;
$z-modal: 1050;
$z-popover: 1060;
$z-tooltip: 1070;
$z-notification: 1080;
// ============================================
// 9. Component-Specific Variables
// ============================================
// Buttons
$button-height-sm: 2rem; // 32px
$button-height-md: 2.5rem; // 40px
$button-height-lg: 3rem; // 48px
$button-padding-x: 1.5rem; // 24px
$button-border-width: 0;
// Cards
$card-padding: 1.5rem;
$card-background: $neutral-100;
$card-border-radius: $radius-xl;
// Inputs
$input-height: 2.75rem; // 44px
$input-padding-x: 1rem; // 16px
$input-border-width: 0;
$input-background: $neutral-100;
$input-border-radius: $radius-lg;
// Sidebar
$sidebar-width: 280px;
$sidebar-width-collapsed: 80px;
$sidebar-background: $neutral-100;
// ============================================
// 10. Mixins
// ============================================
@mixin neumorphic($size: 'md', $type: 'raised', $dark: false) {
@if $dark {
@if $type == 'raised' {
@if $size == 'sm' {
box-shadow: $shadow-dark-soft-sm;
} @else if $size == 'md' {
box-shadow: $shadow-dark-soft-md;
} @else if $size == 'lg' {
box-shadow: $shadow-dark-soft-lg;
}
} @else if $type == 'pressed' {
box-shadow: $shadow-dark-inset-md;
}
} @else {
@if $type == 'raised' {
@if $size == 'sm' {
box-shadow: $shadow-soft-sm;
} @else if $size == 'md' {
box-shadow: $shadow-soft-md;
} @else if $size == 'lg' {
box-shadow: $shadow-soft-lg;
} @else if $size == 'xl' {
box-shadow: $shadow-soft-xl;
}
} @else if $type == 'pressed' {
@if $size == 'sm' {
box-shadow: $shadow-inset-sm;
} @else if $size == 'md' {
box-shadow: $shadow-inset-md;
} @else if $size == 'lg' {
box-shadow: $shadow-inset-lg;
}
}
}
}
@mixin hover-lift($amount: 2px) {
transition: transform $transition-base, box-shadow $transition-base;
&:hover:not(:disabled) {
transform: translateY(-$amount);
}
}
@mixin focus-ring($color: $primary-500) {
&:focus-visible {
outline: none;
box-shadow: 0 0 0 3px rgba($color, 0.2);
}
}
@mixin truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
@mixin responsive($breakpoint) {
@if $breakpoint == 'sm' {
@media (min-width: $breakpoint-sm) { @content; }
} @else if $breakpoint == 'md' {
@media (min-width: $breakpoint-md) { @content; }
} @else if $breakpoint == 'lg' {
@media (min-width: $breakpoint-lg) { @content; }
} @else if $breakpoint == 'xl' {
@media (min-width: $breakpoint-xl) { @content; }
} @else if $breakpoint == '2xl' {
@media (min-width: $breakpoint-2xl) { @content; }
}
}
// ============================================
// 11. Dark Mode Support
// ============================================
@mixin dark-mode {
@media (prefers-color-scheme: dark) {
@content;
}
.dark & {
@content;
}
}
// ============================================
// 12. Utility Classes
// ============================================
.neumorphic-raised {
@include neumorphic('md', 'raised');
}
.neumorphic-pressed {
@include neumorphic('md', 'pressed');
}
.neumorphic-card {
background: $neutral-100;
border-radius: $radius-xl;
padding: $card-padding;
@include neumorphic('md', 'raised');
}
.neumorphic-button {
background: $neutral-100;
border-radius: $radius-lg;
padding: $space-3 $space-6;
@include neumorphic('sm', 'raised');
transition: all $transition-base;
&:hover {
@include neumorphic('md', 'raised');
}
&:active {
@include neumorphic('sm', 'pressed');
}
}
.text-gradient {
background: linear-gradient(135deg, $primary-600, $primary-800);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}

View File

@ -162,8 +162,8 @@ export default defineNuxtConfig({
monacousa: {
dark: false,
colors: {
// Monaco Red Spectrum (from design-system.md)
primary: "#dc2626", // monaco-red-600
// Refined Monaco Red Spectrum
primary: "#dc2626", // Professional primary
'primary-50': "#fef2f2",
'primary-100': "#fee2e2",
'primary-200': "#fecaca",
@ -175,40 +175,41 @@ export default defineNuxtConfig({
'primary-800': "#991b1b",
'primary-900': "#7f1d1d",
// Neutral Palette
secondary: "#ffffff",
accent: "#f4f4f5",
background: "#ffffff",
surface: "#fafafa",
'on-background': "#18181b",
'on-surface': "#18181b",
// Improved Neutral Palette
secondary: "#64748b", // Neutral gray for secondary
accent: "#dc2626", // Monaco red as accent
background: "#fafafa", // Light gray background
surface: "#ffffff", // Pure white surfaces
'on-background': "#1f2937", // Darker text on background
'on-surface': "#1f2937", // Darker text on surface
// Semantic Colors
error: "#ef4444",
// Semantic Colors - More Professional
error: "#dc2626",
warning: "#f59e0b",
info: "#0ea5e9",
success: "#10b981",
info: "#3b82f6",
success: "#22c55e",
// Custom Properties for Glass Effects
'glass-bg': "rgba(255, 255, 255, 0.7)",
'glass-border': "rgba(255, 255, 255, 0.3)",
'glass-dark': "rgba(0, 0, 0, 0.7)",
'glass-bg': "rgba(255, 255, 255, 0.85)",
'glass-border': "rgba(255, 255, 255, 0.18)",
'glass-dark': "rgba(17, 24, 39, 0.6)",
},
variables: {
'border-color': '#e4e4e7',
'border-opacity': 0.12,
'high-emphasis-opacity': 0.87,
'medium-emphasis-opacity': 0.60,
'disabled-opacity': 0.38,
'idle-opacity': 0.04,
'hover-opacity': 0.08,
'focus-opacity': 0.12,
'selected-opacity': 0.12,
'activated-opacity': 0.12,
'pressed-opacity': 0.16,
'dragged-opacity': 0.08,
'shadow-glass': '0 8px 32px rgba(0, 0, 0, 0.1)',
'shadow-monaco': '0 10px 40px rgba(220, 38, 38, 0.15)',
'border-color': '#e5e7eb',
'border-opacity': 0.08,
'high-emphasis-opacity': 0.95,
'medium-emphasis-opacity': 0.70,
'disabled-opacity': 0.45,
'idle-opacity': 0.02,
'hover-opacity': 0.04,
'focus-opacity': 0.08,
'selected-opacity': 0.08,
'activated-opacity': 0.10,
'pressed-opacity': 0.12,
'dragged-opacity': 0.06,
'shadow-glass': '0 8px 32px rgba(31, 41, 55, 0.08)',
'shadow-monaco': '0 10px 40px rgba(185, 28, 28, 0.1)',
'shadow-elevated': '0 20px 25px -5px rgba(0, 0, 0, 0.1)',
},
},
monacousa_dark: {
@ -246,26 +247,62 @@ export default defineNuxtConfig({
VCard: {
elevation: 0,
rounded: 'xl',
class: 'card-modern',
},
VBtn: {
elevation: 0,
rounded: 'xl',
rounded: 'lg',
class: 'text-none font-medium',
size: 'default',
density: 'comfortable',
},
VNavigationDrawer: {
elevation: 0,
class: 'sidebar-modern',
},
VAppBar: {
elevation: 0,
flat: true,
class: 'appbar-modern',
density: 'comfortable',
},
VTextField: {
variant: 'outlined',
rounded: 'xl',
rounded: 'lg',
density: 'comfortable',
class: 'input-modern',
},
VSelect: {
variant: 'outlined',
rounded: 'xl',
rounded: 'lg',
density: 'comfortable',
class: 'select-modern',
},
VDataTable: {
class: 'table-modern',
fixedHeader: true,
hover: true,
},
VChip: {
rounded: 'lg',
size: 'default',
class: 'chip-modern',
},
VDialog: {
class: 'dialog-modern',
maxWidth: '600',
},
VAlert: {
rounded: 'lg',
variant: 'tonal',
class: 'alert-modern',
},
VProgressLinear: {
rounded: true,
height: '6',
},
VProgressCircular: {
width: '3',
},
},
},

View File

@ -1,175 +0,0 @@
<template>
<div class="design-test-page">
<div class="container">
<h1 class="page-title">New Design System Test Pages</h1>
<p class="page-subtitle">Testing implementation of the new Neumorphic design with morphing dropdowns</p>
<div class="navigation-grid">
<NuxtLink to="/admin/dashboard-v2" class="nav-card neumorphic-card">
<Icon name="mdi:view-dashboard" class="nav-icon" />
<h2>Admin Dashboard V2</h2>
<p>New admin dashboard with neumorphic design and morphing dropdowns</p>
</NuxtLink>
<NuxtLink to="/board/dashboard-v2" class="nav-card neumorphic-card">
<Icon name="mdi:chart-box" class="nav-icon" />
<h2>Board Dashboard V2</h2>
<p>Executive dashboard with KPIs and strategic insights</p>
</NuxtLink>
<NuxtLink to="/admin/dashboard" class="nav-card neumorphic-card">
<Icon name="mdi:view-dashboard-outline" class="nav-icon" />
<h2>Current Admin Dashboard</h2>
<p>Compare with the existing admin dashboard</p>
</NuxtLink>
<NuxtLink to="/board/dashboard" class="nav-card neumorphic-card">
<Icon name="mdi:chart-box-outline" class="nav-icon" />
<h2>Current Board Dashboard</h2>
<p>Compare with the existing board dashboard</p>
</NuxtLink>
</div>
<div class="design-info">
<h3>Design System Features</h3>
<ul>
<li> Neumorphic cards and buttons with soft shadows</li>
<li>💧 Glassmorphic blue morphing dropdowns</li>
<li>🎯 Monaco red primary color maintained</li>
<li>📱 Responsive grid layouts</li>
<li>🎨 Professional and inviting aesthetic</li>
<li> Smooth spring animations</li>
</ul>
</div>
</div>
</div>
</template>
<script setup>
// No authentication required for testing
definePageMeta({
auth: false,
layout: false
})
</script>
<style lang="scss" scoped>
// Import the new design system
@import '@/design-mockups/styles/design-system.scss';
.design-test-page {
min-height: 100vh;
background: linear-gradient(135deg, $neutral-50 0%, $neutral-100 100%);
padding: 3rem 1rem;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
.page-title {
text-align: center;
font-size: $text-4xl;
font-weight: $font-bold;
background: linear-gradient(135deg, $primary-600, $primary-800);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 1rem;
}
.page-subtitle {
text-align: center;
color: $neutral-600;
font-size: $text-lg;
margin-bottom: 3rem;
}
.navigation-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
margin-bottom: 3rem;
}
.nav-card {
@include neumorphic-card('md');
padding: 2rem;
text-decoration: none;
color: inherit;
transition: all $transition-base;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
&:hover {
@include neumorphic-card('lg');
transform: translateY(-4px);
.nav-icon {
transform: scale(1.1);
color: $primary-600;
}
}
.nav-icon {
width: 48px;
height: 48px;
color: $neutral-600;
margin-bottom: 1rem;
transition: all $transition-base;
}
h2 {
font-size: $text-xl;
font-weight: $font-semibold;
color: $neutral-800;
margin-bottom: 0.5rem;
}
p {
font-size: $text-sm;
color: $neutral-600;
line-height: $leading-relaxed;
}
}
.design-info {
@include neumorphic-card('lg');
padding: 2rem;
max-width: 600px;
margin: 0 auto;
h3 {
font-size: $text-xl;
font-weight: $font-semibold;
color: $neutral-800;
margin-bottom: 1.5rem;
}
ul {
list-style: none;
padding: 0;
li {
padding: 0.75rem 0;
color: $neutral-700;
font-size: $text-base;
border-bottom: 1px solid rgba($neutral-200, 0.5);
&:last-child {
border-bottom: none;
}
}
}
}
// Neumorphic Elements
.neumorphic-card {
background: linear-gradient(145deg, #ffffff, #f0f0f0);
border-radius: $radius-xl;
box-shadow: $shadow-soft-md;
}
</style>