# ๐ฑ RESPONSIVE - Documentazione Completa NetGescon > **Sistema Responsive dell'interfaccia universale** > **๐ Creato:** 21/07/2025 > **๐ Ultimo aggiornamento:** 21/07/2025 > **๐ฏ Prioritร :** โก ALTO --- ## ๐ **OVERVIEW RESPONSIVE SYSTEM** Il Sistema Responsive NetGescon garantisce **ESPERIENZA OTTIMALE** su: - ๐ฑ **Mobile Devices** (320px - 767px) - ๐ **Tablet Devices** (768px - 1023px) - ๐ป **Desktop Screens** (1024px - 1439px) - ๐ฅ๏ธ **Large Displays** (1440px+) - ๐บ **Ultra-wide Monitors** (2560px+) ### ๐ฏ **Responsive Philosophy** - **Mobile First** - Design ottimizzato prima per mobile - **Progressive Enhancement** - Funzionalitร aggiuntive su schermi piรน grandi - **Fluid Layouts** - Adattamento fluido a qualsiasi dimensione - **Touch Friendly** - Interfaccia ottimizzata per touch --- ## ๐ **BREAKPOINT SYSTEM** ### ๐จ **CSS Breakpoints** ```css /* /public/css/responsive/breakpoints.css */ :root { /* === BREAKPOINT DEFINITIONS === */ --netgescon-breakpoint-xs: 320px; /* Extra Small devices */ --netgescon-breakpoint-sm: 640px; /* Small devices */ --netgescon-breakpoint-md: 768px; /* Medium devices */ --netgescon-breakpoint-lg: 1024px; /* Large devices */ --netgescon-breakpoint-xl: 1280px; /* Extra Large devices */ --netgescon-breakpoint-2xl: 1536px; /* 2X Large devices */ --netgescon-breakpoint-3xl: 1920px; /* 3X Large devices */ } /* === MEDIA QUERY MIXINS === */ @media (max-width: 639px) { /* Mobile styles */ .mobile-only { display: block !important; } .tablet-up { display: none !important; } .desktop-up { display: none !important; } } @media (min-width: 640px) and (max-width: 767px) { /* Small tablet styles */ .mobile-only { display: none !important; } .tablet-only { display: block !important; } } @media (min-width: 768px) { /* Tablet and up */ .mobile-only { display: none !important; } .tablet-up { display: block !important; } } @media (min-width: 1024px) { /* Desktop and up */ .tablet-only { display: none !important; } .desktop-up { display: block !important; } } @media (min-width: 1280px) { /* Large desktop */ .desktop-only { display: none !important; } .large-desktop-up { display: block !important; } } ``` ### ๐ฑ **JavaScript Breakpoints** ```javascript // /public/js/responsive/breakpoints.js class ResponsiveManager { constructor() { this.breakpoints = { xs: 320, sm: 640, md: 768, lg: 1024, xl: 1280, '2xl': 1536, '3xl': 1920 }; this.currentBreakpoint = this.getCurrentBreakpoint(); this.orientation = this.getOrientation(); this.init(); } init() { // Listen for resize events window.addEventListener('resize', this.handleResize.bind(this)); // Listen for orientation change window.addEventListener('orientationchange', this.handleOrientationChange.bind(this)); // Initial setup this.updateLayout(); } getCurrentBreakpoint() { const width = window.innerWidth; if (width >= this.breakpoints['3xl']) return '3xl'; if (width >= this.breakpoints['2xl']) return '2xl'; if (width >= this.breakpoints.xl) return 'xl'; if (width >= this.breakpoints.lg) return 'lg'; if (width >= this.breakpoints.md) return 'md'; if (width >= this.breakpoints.sm) return 'sm'; return 'xs'; } getOrientation() { return window.innerHeight > window.innerWidth ? 'portrait' : 'landscape'; } handleResize() { const newBreakpoint = this.getCurrentBreakpoint(); const newOrientation = this.getOrientation(); if (newBreakpoint !== this.currentBreakpoint || newOrientation !== this.orientation) { this.currentBreakpoint = newBreakpoint; this.orientation = newOrientation; this.updateLayout(); // Dispatch custom event document.dispatchEvent(new CustomEvent('netgescon:breakpoint-change', { detail: { breakpoint: this.currentBreakpoint, orientation: this.orientation, width: window.innerWidth, height: window.innerHeight } })); } } handleOrientationChange() { setTimeout(() => { this.handleResize(); }, 100); } updateLayout() { document.body.className = document.body.className .replace(/breakpoint-\w+/g, '') .replace(/orientation-\w+/g, ''); document.body.classList.add(`breakpoint-${this.currentBreakpoint}`); document.body.classList.add(`orientation-${this.orientation}`); // Update CSS custom property document.documentElement.style.setProperty('--current-breakpoint', this.currentBreakpoint); } isMobile() { return ['xs', 'sm'].includes(this.currentBreakpoint); } isTablet() { return this.currentBreakpoint === 'md'; } isDesktop() { return ['lg', 'xl', '2xl', '3xl'].includes(this.currentBreakpoint); } isTouch() { return 'ontouchstart' in window || navigator.maxTouchPoints > 0; } } // Initialize responsive manager const ResponsiveManager = new ResponsiveManager(); window.ResponsiveManager = ResponsiveManager; ``` --- ## ๐๏ธ **LAYOUT SYSTEM** ### ๐ฑ **Mobile Layout (320px - 767px)** ```css /* /public/css/responsive/mobile.css */ @media (max-width: 767px) { /* === LAYOUT STRUCTURE === */ .netgescon-layout { display: flex; flex-direction: column; min-height: 100vh; } /* Header mobile */ .netgescon-header { position: fixed; top: 0; left: 0; right: 0; height: 60px; z-index: 1000; padding: 0 1rem; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); } /* Sidebar mobile */ .netgescon-sidebar { position: fixed; top: 60px; left: -280px; width: 280px; height: calc(100vh - 60px); background: white; box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15); transition: left 0.3s ease; z-index: 999; overflow-y: auto; } .netgescon-sidebar.show { left: 0; } /* Content mobile */ .netgescon-content { margin-top: 60px; padding: 1rem; flex: 1; min-height: calc(100vh - 60px); } /* Footer mobile */ .netgescon-footer { display: none; /* Hidden on mobile */ } /* === OVERLAY === */ .sidebar-overlay { position: fixed; top: 60px; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.5); opacity: 0; visibility: hidden; transition: all 0.3s ease; z-index: 998; } .sidebar-overlay.show { opacity: 1; visibility: visible; } /* === MOBILE NAVIGATION === */ .mobile-nav-toggle { display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; background: none; border: none; color: var(--netgescon-gray-600); font-size: 1.25rem; cursor: pointer; } .mobile-nav-toggle:hover { color: var(--netgescon-primary); background: var(--netgescon-gray-100); border-radius: var(--netgescon-border-radius); } /* === MOBILE CARDS === */ .netgescon-card { margin-bottom: 1rem; padding: 1rem; } .stats-grid { grid-template-columns: 1fr; gap: 1rem; } .content-grid { grid-template-columns: 1fr; gap: 1.5rem; } /* === MOBILE FORMS === */ .form-row { flex-direction: column; } .form-group { margin-bottom: 1rem; } .btn-group { flex-direction: column; gap: 0.5rem; } .btn-group .btn { width: 100%; justify-content: center; } /* === MOBILE TABLES === */ .table-responsive-mobile { display: block; width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; } .mobile-table-stack { display: none; } @media (max-width: 576px) { .table-responsive-mobile table { display: none; } .mobile-table-stack { display: block; } .mobile-table-item { background: white; border: 1px solid var(--netgescon-gray-200); border-radius: var(--netgescon-border-radius); padding: 1rem; margin-bottom: 1rem; } .mobile-table-item .item-header { font-weight: var(--netgescon-font-semibold); color: var(--netgescon-gray-900); margin-bottom: 0.5rem; } .mobile-table-item .item-row { display: flex; justify-content: space-between; align-items: center; padding: 0.25rem 0; border-bottom: 1px solid var(--netgescon-gray-100); } .mobile-table-item .item-row:last-child { border-bottom: none; } .mobile-table-item .item-label { font-weight: var(--netgescon-font-medium); color: var(--netgescon-gray-600); font-size: var(--netgescon-text-sm); } .mobile-table-item .item-value { color: var(--netgescon-gray-900); text-align: right; } } } ``` ### ๐ **Tablet Layout (768px - 1023px)** ```css /* /public/css/responsive/tablet.css */ @media (min-width: 768px) and (max-width: 1023px) { /* === LAYOUT STRUCTURE === */ .netgescon-layout { display: grid; grid-template-areas: "header header" "sidebar content" "sidebar footer"; grid-template-columns: 260px 1fr; grid-template-rows: 70px 1fr auto; min-height: 100vh; } /* Header tablet */ .netgescon-header { grid-area: header; position: sticky; top: 0; z-index: 100; } /* Sidebar tablet */ .netgescon-sidebar { grid-area: sidebar; width: 260px; position: sticky; top: 70px; height: calc(100vh - 70px); overflow-y: auto; border-right: 1px solid var(--netgescon-gray-200); } .netgescon-sidebar.collapsed { width: 70px; grid-template-columns: 70px 1fr; } /* Content tablet */ .netgescon-content { grid-area: content; padding: 1.5rem; overflow-x: auto; } /* Footer tablet */ .netgescon-footer { grid-area: footer; border-top: 1px solid var(--netgescon-gray-200); } /* === TABLET CARDS === */ .stats-grid { grid-template-columns: repeat(2, 1fr); gap: 1.5rem; } .content-grid { grid-template-columns: repeat(2, 1fr); gap: 2rem; } /* === TABLET FORMS === */ .form-row { display: flex; gap: 1rem; } .form-row .form-group { flex: 1; } .btn-group { flex-direction: row; gap: 1rem; } /* === TABLET MODALS === */ .modal-dialog { max-width: 600px; margin: 2rem auto; } .modal-dialog-lg { max-width: 800px; } } ``` ### ๐ป **Desktop Layout (1024px+)** ```css /* /public/css/responsive/desktop.css */ @media (min-width: 1024px) { /* === LAYOUT STRUCTURE === */ .netgescon-layout { display: grid; grid-template-areas: "header header" "sidebar content" "sidebar footer"; grid-template-columns: 280px 1fr; grid-template-rows: 70px 1fr 70px; min-height: 100vh; } /* Header desktop */ .netgescon-header { grid-area: header; position: sticky; top: 0; z-index: 100; } /* Sidebar desktop */ .netgescon-sidebar { grid-area: sidebar; width: 280px; position: sticky; top: 70px; height: calc(100vh - 140px); overflow-y: auto; border-right: 1px solid var(--netgescon-gray-200); } .netgescon-sidebar.collapsed { width: 70px; .sidebar-text { display: none; } .sidebar-icon { margin: 0 auto; } } /* Content desktop */ .netgescon-content { grid-area: content; padding: 2rem; overflow-x: auto; } /* Footer desktop */ .netgescon-footer { grid-area: footer; height: 70px; border-top: 1px solid var(--netgescon-gray-200); } /* === DESKTOP CARDS === */ .stats-grid { grid-template-columns: repeat(4, 1fr); gap: 2rem; } .content-grid { grid-template-columns: repeat(3, 1fr); gap: 2rem; } /* === DESKTOP FORMS === */ .form-row { display: flex; gap: 1.5rem; } .form-row .form-group { flex: 1; } .form-row .form-group.col-2 { flex: 2; } .form-row .form-group.col-3 { flex: 3; } /* === DESKTOP MODALS === */ .modal-dialog { max-width: 800px; margin: 3rem auto; } .modal-dialog-lg { max-width: 1000px; } .modal-dialog-xl { max-width: 1200px; } /* === DESKTOP TOOLTIPS === */ .tooltip { display: block; } /* === DESKTOP HOVER EFFECTS === */ .netgescon-card:hover { transform: translateY(-2px); box-shadow: var(--netgescon-shadow-lg); } .btn:hover { transform: translateY(-1px); box-shadow: var(--netgescon-shadow-md); } } ``` --- ## ๐ฑ **MOBILE ENHANCEMENTS** ### ๐ **Touch Interactions** ```css /* /public/css/responsive/touch.css */ /* === TOUCH OPTIMIZATIONS === */ @media (hover: none) and (pointer: coarse) { /* Touch device specific styles */ /* Larger touch targets */ .btn { min-height: 44px; min-width: 44px; padding: 12px 16px; } .form-input { min-height: 44px; padding: 12px; font-size: 16px; /* Prevent zoom on iOS */ } .form-select { min-height: 44px; padding: 12px; font-size: 16px; } /* Touch-friendly navigation */ .nav-link { padding: 12px 16px; min-height: 44px; display: flex; align-items: center; } /* Touch-friendly table rows */ .table tbody tr { min-height: 48px; } .table tbody td { padding: 12px 8px; } /* Remove hover effects on touch */ .netgescon-card:hover { transform: none; box-shadow: var(--netgescon-shadow); } .btn:hover { transform: none; box-shadow: none; } /* Touch feedback */ .btn:active { transform: scale(0.98); background-color: var(--netgescon-primary-dark); } .netgescon-card:active { transform: scale(0.99); } } ``` ### ๐ฑ **Mobile Navigation** ```javascript // /public/js/responsive/mobile-nav.js class MobileNavigation { constructor() { this.sidebar = document.getElementById('sidebar'); this.overlay = document.getElementById('sidebar-overlay'); this.toggleBtn = document.getElementById('mobile-nav-toggle'); this.isOpen = false; this.init(); } init() { if (!this.sidebar || !this.toggleBtn) return; // Create overlay if it doesn't exist if (!this.overlay) { this.createOverlay(); } // Bind events this.toggleBtn.addEventListener('click', this.toggle.bind(this)); this.overlay.addEventListener('click', this.close.bind(this)); // Close on escape key document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && this.isOpen) { this.close(); } }); // Close on orientation change window.addEventListener('orientationchange', () => { setTimeout(() => this.close(), 100); }); // Close when switching to desktop window.addEventListener('resize', () => { if (window.innerWidth >= 768 && this.isOpen) { this.close(); } }); } createOverlay() { this.overlay = document.createElement('div'); this.overlay.id = 'sidebar-overlay'; this.overlay.className = 'sidebar-overlay'; document.body.appendChild(this.overlay); } toggle() { if (this.isOpen) { this.close(); } else { this.open(); } } open() { this.isOpen = true; this.sidebar.classList.add('show'); this.overlay.classList.add('show'); document.body.style.overflow = 'hidden'; // Trap focus in sidebar this.trapFocus(); // Dispatch event document.dispatchEvent(new CustomEvent('netgescon:mobile-nav:open')); } close() { this.isOpen = false; this.sidebar.classList.remove('show'); this.overlay.classList.remove('show'); document.body.style.overflow = ''; // Release focus trap this.releaseFocus(); // Dispatch event document.dispatchEvent(new CustomEvent('netgescon:mobile-nav:close')); } trapFocus() { const focusableElements = this.sidebar.querySelectorAll( 'a[href], button, textarea, input[type="text"], input[type="radio"], input[type="checkbox"], select' ); if (focusableElements.length === 0) return; const firstElement = focusableElements[0]; const lastElement = focusableElements[focusableElements.length - 1]; this.focusHandler = (e) => { if (e.key === 'Tab') { if (e.shiftKey) { if (document.activeElement === firstElement) { e.preventDefault(); lastElement.focus(); } } else { if (document.activeElement === lastElement) { e.preventDefault(); firstElement.focus(); } } } }; document.addEventListener('keydown', this.focusHandler); firstElement.focus(); } releaseFocus() { if (this.focusHandler) { document.removeEventListener('keydown', this.focusHandler); this.focusHandler = null; } } } // Initialize mobile navigation document.addEventListener('DOMContentLoaded', () => { if (window.innerWidth < 768) { new MobileNavigation(); } }); ``` --- ## ๐ **RESPONSIVE TABLES** ### ๐ฑ **Mobile Table Stack** ```javascript // /public/js/responsive/responsive-tables.js class ResponsiveTables { constructor() { this.tables = document.querySelectorAll('.table-responsive-mobile'); this.init(); } init() { this.tables.forEach(tableContainer => { this.enhanceTable(tableContainer); }); // Re-initialize on breakpoint change document.addEventListener('netgescon:breakpoint-change', (e) => { if (e.detail.breakpoint === 'xs') { this.createMobileViews(); } else if (e.detail.breakpoint !== 'xs') { this.removeMobileViews(); } }); } enhanceTable(container) { const table = container.querySelector('table'); if (!table) return; // Create mobile stack version this.createMobileStack(container, table); // Add horizontal scroll indicators this.addScrollIndicators(container); } createMobileStack(container, table) { const mobileStack = document.createElement('div'); mobileStack.className = 'mobile-table-stack'; const headers = Array.from(table.querySelectorAll('thead th')).map(th => th.textContent.trim()); const rows = table.querySelectorAll('tbody tr'); rows.forEach(row => { const cells = row.querySelectorAll('td'); const mobileItem = document.createElement('div'); mobileItem.className = 'mobile-table-item'; // Create header if there's a primary column const primaryCell = cells[0]; if (primaryCell) { const itemHeader = document.createElement('div'); itemHeader.className = 'item-header'; itemHeader.textContent = primaryCell.textContent.trim(); mobileItem.appendChild(itemHeader); } // Create rows for each cell cells.forEach((cell, index) => { if (index === 0) return; // Skip primary cell const itemRow = document.createElement('div'); itemRow.className = 'item-row'; const label = document.createElement('div'); label.className = 'item-label'; label.textContent = headers[index] || `Campo ${index}`; const value = document.createElement('div'); value.className = 'item-value'; value.innerHTML = cell.innerHTML; itemRow.appendChild(label); itemRow.appendChild(value); mobileItem.appendChild(itemRow); }); mobileStack.appendChild(mobileItem); }); container.appendChild(mobileStack); } addScrollIndicators(container) { const table = container.querySelector('table'); if (!table) return; const indicator = document.createElement('div'); indicator.className = 'scroll-indicator'; indicator.innerHTML = ' Scorri per vedere piรน colonne'; container.appendChild(indicator); // Hide indicator when scrolled to end container.addEventListener('scroll', () => { const isScrolledToEnd = container.scrollLeft >= (container.scrollWidth - container.clientWidth - 10); indicator.style.opacity = isScrolledToEnd ? '0' : '1'; }); } createMobileViews() { this.tables.forEach(container => { container.classList.add('mobile-view'); }); } removeMobileViews() { this.tables.forEach(container => { container.classList.remove('mobile-view'); }); } } // Initialize responsive tables document.addEventListener('DOMContentLoaded', () => { new ResponsiveTables(); }); ``` --- ## ๐จ **RESPONSIVE COMPONENTS** ### ๐ฑ **Responsive Cards** ```blade @props([ 'title' => '', 'mobileTitle' => null, 'content' => '', 'actions' => [], 'mobileLayout' => 'stack' ])