# π― Prompt Windsurf β ObsiViewer Nimbus UI (Desktop + Mobile)
## ObsiViewer β Interface "Nimbus-like" 100% Responsive
**RΓ΄le & mode :** Agis comme **Staff Frontend Engineer Angular 20 + UX Designer** expert. Raisonnement dΓ©taillΓ© autorisΓ©. Tu as les pleins pouvoirs de refactor UI, d'ajout de composants, et de migration CSS vers Tailwind. Conserve la compatibilitΓ© de toutes features existantes.
**Contrainte majeure :** L'interface doit Γͺtre **100% responsive** (Desktop + Mobile + Tablet). Un **bouton toggle** dans la navbar permet de basculer entre l'ancienne interface et la nouvelle sans perte d'Γ©tat.
---
## Contexte
* **Projet** : ObsiViewer (Angular 20 + Tailwind, Node/Express backend)
* **Objectif** : Refondre l'interface selon un design **Nimbus Notes/FuseBase**-like avec responsive design complet
* **CΕurs d'usage** : navigation par **dossiers**, **tags**, **recherche**, **lecture markdown** plein Γ©cran, **ToC** Γ droite, **tri et filtres** rapides[1][2][3]
* **NouveautΓ©** : Design adaptatif complet (Desktop/Mobile/Tablet) avec UI toggle persistΓ©
***
## π― Architecture Responsive Cible
### Desktop (β₯1024px) - Layout 3 Colonnes
```
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β NAVBAR (Dark, fixed, h-14) + Toggle UI β
ββββββββββββββββββ¬βββββββββββββββββββ¬βββββββββββββββββββββββ€
β β β β
β SIDEBAR β RESULT LIST β NOTE VIEW + TOC β
β (240-440px) β (virtualized) β (Resizable) β
β Resizable β β β
β β β β
β - Quick Links β - Search bar β - Markdown β
β - Folders β - Chips filters β - ToC drawer β
β - Tags β - Items (80px) β - Actions bar β
β β - Pagination β β
β β β β
ββββββββββββββββββ΄βββββββββββββββββββ΄βββββββββββββββββββββββ
```
### Mobile (<768px) - Navigation par Onglets
```
ββββββββββββββββββββββββββββββββββββ
β NAVBAR (compact, h-12) β
β [β‘] [Search] [Toggle] β
ββββββββββββββββββββββββββββββββββββ€
β β
β CONTENT AREA (Full-width) β
β - Drawer sidebar (80vw gauche) β
β - Liste swipeable (tab liste) β
β - Markdown full-screen (page) β
β - ToC inline (toggle button) β
β β
ββββββββββββββββββββββββββββββββββββ€
β BOTTOM NAVIGATION (sticky) β
β [π] [π] [π] [π] (4 onglets) β
ββββββββββββββββββββββββββββββββββββ
```
### Tablet (768px β€ width < 1024px) - Hybride
```
ββββββββββββββββββββββββββββββββββββββββ
β NAVBAR + Toggle (fixed, h-14) β
ββββββββββββββββββββββββββββββββββββββββ€
β TAB NAVIGATION (3 onglets) β
β [Sidebar] [List] [Page] β
ββββββββββββββββββββββββββββββββββββββββ€
β β
β ACTIVE TAB CONTENT (scrollable) β
β - Panneau full-width par tab β
β - Drawer si besoin β
β β
ββββββββββββββββββββββββββββββββββββββββ
```
***
## π Architecture Feature Flag & Toggle
### 1) Toggle UI dans la NavBar
Ajouter un **bouton toggle** dans `src/app/layout/app-navbar/app-navbar.component.ts` :[4]
```html
```
### 2) Service de Gestion du Mode UI
CrΓ©er `src/app/shared/services/ui-mode.service.ts` :
```typescript
import { Injectable, signal, effect } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class UiModeService {
// Signal pour rΓ©activitΓ© fine-grained[3]
isNimbusMode = signal(this.loadUIMode());
constructor() {
// Persister les changements automatiquement
effect(() => {
if (typeof localStorage !== 'undefined') {
localStorage.setItem('obsiviewer-ui-mode',
this.isNimbusMode() ? 'nimbus' : 'legacy');
}
});
}
toggleUIMode() {
const newMode = !this.isNimbusMode();
this.isNimbusMode.set(newMode);
}
private loadUIMode(): boolean {
if (typeof localStorage === 'undefined') return true;
const saved = localStorage.getItem('obsiviewer-ui-mode');
return saved ? saved === 'nimbus' : true; // Nimbus par dΓ©faut
}
}
```
### 3) Layout Wrapper avec Feature Flag
CrΓ©er `src/app/layout/app-shell-adaptive/app-shell-adaptive.component.ts` :
```typescript
import { Component, inject } from '@angular/core';
import { UiModeService } from '@app/shared/services/ui-mode.service';
import { AppShellNimbusLayoutComponent } from '../app-shell-nimbus/app-shell-nimbus.component';
import { AppShellLegacyLayoutComponent } from '../app-shell-legacy/app-shell-legacy.component';
@Component({
selector: 'app-shell-adaptive',
template: `
@if (uiMode.isNimbusMode()) {
} @else {
}
`,
standalone: true,
imports: [AppShellNimbusLayoutComponent, AppShellLegacyLayoutComponent],
})
export class AppShellAdaptiveComponent {
uiMode = inject(UiModeService);
}
```
***
## π¨ Responsive Design Strategy
### Breakpoints Tailwind (Mobile-First)[5][6]
```javascript
// tailwind.config.js
module.exports = {
theme: {
screens: {
'xs': '320px', // iPhone SE
'sm': '640px', // Petites tablettes
'md': '768px', // iPad, tablettes
'lg': '1024px', // Desktop compact
'xl': '1280px', // Desktop standard
'2xl': '1536px', // Larges Γ©crans
},
extend: {
// Tokens personnalisΓ©s Nimbus-like
colors: {
nimbus: {
50: '#f0f9ff',
500: '#0ea5e9', // Turquoise actions
900: '#0c4a6e' // Dark mode
}
}
}
},
};
```
### Service de DΓ©tection Responsive
```typescript
// src/shared/services/breakpoint.service.ts
import { Injectable, signal, inject } from '@angular/core';
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { map } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class ResponsiveService {
private breakpointObserver = inject(BreakpointObserver);
// Signaux rΓ©actifs pour chaque breakpoint[25][28]
isMobile = signal(false);
isTablet = signal(false);
isDesktop = signal(false);
constructor() {
// Mobile (< 768px)
this.breakpointObserver.observe('(max-width: 767px)').subscribe(result => {
this.isMobile.set(result.matches);
});
// Tablet (768px - 1023px)
this.breakpointObserver.observe('(min-width: 768px) and (max-width: 1023px)').subscribe(result => {
this.isTablet.set(result.matches);
});
// Desktop (>= 1024px)
this.breakpointObserver.observe('(min-width: 1024px)').subscribe(result => {
this.isDesktop.set(result.matches);
});
}
}
```
***
## π± Composants Responsifs SpΓ©cifiques
### 1) Shell Principal Nimbus (Responsive)
```typescript
// app-shell-nimbus.component.ts
import { Component, inject } from '@angular/core';
import { ResponsiveService } from '@app/shared/services/responsive.service';
@Component({
selector: 'app-shell-nimbus-layout',
template: `
`,
standalone: true,
imports: [/* tous les composants */],
})
export class AppShellNimbusLayoutComponent {
responsive = inject(ResponsiveService);
}
```
### 2) Bottom Navigation Mobile
```typescript
// app-bottom-navigation.component.ts
import { Component, inject } from '@angular/core';
import { MobileNavService } from '@app/shared/services/mobile-nav.service';
@Component({
selector: 'app-bottom-navigation',
template: `
`,
standalone: true,
})
export class AppBottomNavigationComponent {
mobileNav = inject(MobileNavService);
tabs = [
{ id: 'sidebar', icon: 'π', label: 'Dossiers' },
{ id: 'list', icon: 'π', label: 'Liste' },
{ id: 'page', icon: 'π', label: 'Page' },
{ id: 'toc', icon: 'π', label: 'Sommaire' }
];
setActiveTab(tabId: string) {
this.mobileNav.setActiveTab(tabId as any);
}
}
```
### 3) Service Navigation Mobile
```typescript
// src/shared/services/mobile-nav.service.ts
import { Injectable, signal } from '@angular/core';
type MobileTab = 'sidebar' | 'list' | 'page' | 'toc';
@Injectable({ providedIn: 'root' })
export class MobileNavService {
activeTab = signal('list');
// Γtat des drawers
sidebarOpen = signal(false);
tocOpen = signal(false);
setActiveTab(tab: MobileTab) {
this.activeTab.set(tab);
// Auto-fermer les drawers quand on change d'onglet
if (tab !== 'sidebar') this.sidebarOpen.set(false);
if (tab !== 'toc') this.tocOpen.set(false);
}
toggleSidebar() {
this.sidebarOpen.update(open => !open);
}
toggleToc() {
this.tocOpen.update(open => !open);
}
}
```
### 4) Drawer Sidebar Mobile
```typescript
// app-sidebar-drawer.component.ts
import { Component, inject } from '@angular/core';
import { MobileNavService } from '@app/shared/services/mobile-nav.service';
@Component({
selector: 'app-sidebar-drawer',
template: `
`,
standalone: true,
imports: [AppSidebarContentComponent],
})
export class AppSidebarDrawerComponent {
mobileNav = inject(MobileNavService);
}
```
### 5) Search Bar Responsive
```typescript
// app-search-bar.component.ts
import { Component, inject } from '@angular/core';
import { ResponsiveService } from '@app/shared/services/responsive.service';
import { MobileNavService } from '@app/shared/services/mobile-nav.service';
@Component({
selector: 'app-search-bar',
template: `
`,
standalone: true,
})
export class AppSearchBarComponent {
responsive = inject(ResponsiveService);
mobileNav = inject(MobileNavService);
searchQuery = '';
activeFilters: string[] = [];
openFilters() { /* */ }
removeFilter(filter: string) { /* */ }
openFolderPicker() { /* */ }
openTagPicker() { /* */ }
openPagePicker() { /* */ }
}
```
### 6) Liste de RΓ©sultats Responsive
```typescript
// app-result-list.component.ts
import { Component, inject, Input } from '@angular/core';
import { ResponsiveService } from '@app/shared/services/responsive.service';
@Component({
selector: 'app-result-list',
template: `
dk-virtual-scroll-viewport
[itemSize]="responsive.isMobile() ? 70 : 80"
class="h"h-full">
`,
standalone: true,
imports: [ScrollingModule],
})
export class AppResultListComponent {
responsive = inject(ResponsiveService);
@Input() items: any[] = [];
trackByFn(index: number, item: any) {
return item.id;
}
openNote(item: any) {
// Navigation vers la note
}
}
```
### 7) Item de RΓ©sultat Mobile-Optimized
```typescript
// app-result-list-item.component.ts
@Component({
selector: 'app-result-list-item',
template: `
{{ item.title }}
{{ item.modified | date:'short' }}
{{ tag }}
{{ item.excerpt }}
`,
standalone: true,
})
export class AppResultListItemComponent {
@Input() item: any;
@Input() compact = false;
}
```
### 8) Markdown Viewer avec ToC Responsive
```typescript
// app-markdown-viewer.component.ts
import { Component, inject } from '@angular/core';
import { ResponsiveService } from '@app/shared/services/responsive.service';
import { MobileNavService } from '@app/shared/services/mobile-nav.service';
@Component({
selector: 'app-markdown-viewer',
template: `
{{ currentNote?.title }}
0"
[headings]="headings"
class="fixed right-0 top-14 bottom-0 w-64
bg-gray-50 dark:bg-gray-800 border-l
border-gray-200 dark:border-gray-700">
`,
standalone: true,
})
export class AppMarkdownViewerComponent {
responsive = inject(ResponsiveService);
mobileNav = inject(MobileNavService);
@Input() currentNote: any;
@Input() markdownHTML = '';
@Input() headings: any[] = [];
}
```
***
## π¬ Gestures & Navigation Tactile
### Directive Swipe Navigation
```typescript
// src/shared/directives/swipe-nav.directive.ts
import { Directive, Output, EventEmitter, HostListener } from '@angular/core';
@Directive({
selector: '[appSwipeNav]',
standalone: true,
})
export class SwipeNavDirective {
@Output() swipeLeft = new EventEmitter();
@Output() swipeRight = new EventEmitter();
private startX = 0;
private startY = 0;
private threshold = 50; // Minimum distance
private restraint = 100; // Maximum perpendicular distance
@HostListener('touchstart', ['$event'])
onTouchStart(e: TouchEvent) {
const touch = e.touches[0];
this.startX = touch.clientX;
this.startY = touch.clientY;
}
@HostListener('touchend', ['$event'])
onTouchEnd(e: TouchEvent) {
const touch = e.changedTouches[0];
const endX = touch.clientX;
const endY = touch.clientY;
const distX = this.startX - endX;
const distY = Math.abs(this.startY - endY);
// VΓ©rifier que c'est bien un swipe horizontal
if (Math.abs(distX) >= this.threshold && distY <= this.restraint) {
if (distX > 0) {
this.swipeLeft.emit();
} else {
this.swipeRight.emit();
}
}
}
}
```
### Usage Mobile avec Swipe
```html
```
***
## β‘ Performance & Optimisations Mobile
### Critical Optimizations[7][8]
```typescript
// Lazy loading des images
// app-image-lazy.directive.ts
@Directive({
selector: 'img[appLazyLoad]'
})
export class LazyLoadDirective implements OnInit {
@Input() src!: string;
@Input() alt = '';
ngOnInit() {
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target as HTMLImageElement;
img.src = this.src;
img.alt = this.alt;
observer.unobserve(img);
}
});
});
observer.observe(this.el.nativeElement);
}
}
```
### Skeleton Loaders Mobile
```typescript
// app-skeleton.component.ts
@Component({
selector: 'app-skeleton',
template: `
`,
standalone: true,
})
export class AppSkeletonComponent {
responsive = inject(ResponsiveService);
}
```
***
## π
Plan d'ImplΓ©mentation PriorisΓ©
### Phase 1: Infrastructure (2-3j)
- β
`UiModeService` + localStorage persistence
- β
`ResponsiveService` avec BreakpointObserver[9][10]
- β
`AppShellAdaptiveComponent` wrapper
- β
Toggle button dans navbar
- β
Breakpoints Tailwind mobile-first[6][5]
### Phase 2: Layout Desktop (3-4j)
- β
Desktop layout 3 colonnes (β₯1024px)
- β
Sidebar resizable avec dossiers/tags
- β
Liste virtualisΓ©e performante
- β
ToC panel fixe Γ droite
- β
Search bar avec chips de filtres[2][1]
### Phase 3: Navigation Mobile (2-3j)
- β
`MobileNavService` (state management)
- β
`BottomNavigationComponent` (4 onglets)[11][12]
- β
Tab routing et state persistence
- β
Swipe gestures directive[13][14]
### Phase 4: Composants Mobile (3-4j)
- β
Drawer sidebar mobile[15][16]
- β
Search bar responsive compacte
- β
Result list item mobile-optimized
- β
Markdown viewer full-screen mobile
- β
ToC overlay mobile avec animations
### Phase 5: Tablet & Transitions (1-2j)
- β
Layout hybride tablet (768-1023px)
- β
Animations fluides entre breakpoints
- β
Touch targets β₯ 44px[6]
- β
Gesture handling avancΓ©
### Phase 6: Testing & Polish (2-3j)
- β
Tests E2E (toggle, breakpoints, gestures)
- β
Lighthouse mobile audit (β₯85 perf, β₯95 a11y)
- β
Visual regression (3 breakpoints)
- β
Keyboard navigation complète
- β
WCAG 2.1 AA compliance
**Total estimΓ©** : 13-19 jours (Γ©quipe 1 FE engineer)
***
## ποΈ Structure des Fichiers
```
src/app/
βββ layout/
β βββ app-shell-adaptive/
β β βββ app-shell-adaptive.component.ts # Feature flag wrapper
β βββ app-shell-nimbus/
β β βββ app-shell-nimbus.component.ts # Layout responsive
β β βββ app-shell-nimbus.desktop.html # Template desktop
β β βββ app-shell-nimbus.tablet.html # Template tablet
β β βββ app-shell-nimbus.mobile.html # Template mobile
β βββ app-navbar/
β βββ app-navbar.component.ts
β βββ app-navbar.desktop.html # NavBar desktop
β βββ app-navbar.mobile.html # NavBar mobile compact
β
βββ features/
β βββ sidebar/
β β βββ app-left-sidebar.component.ts # Desktop sidebar
β β βββ app-sidebar-drawer.component.ts # Mobile drawer[39]
β β βββ app-sidebar-content.component.ts # Contenu partagΓ©
β β
β βββ search-bar/
β β βββ app-search-bar.component.ts # Responsive wrapper
β β βββ app-search-desktop.component.ts # Desktop full
β β βββ app-search-mobile.component.ts # Mobile compact
β β
β βββ result-list/
β β βββ app-result-list.component.ts # Liste virtualisΓ©e
β β βββ app-result-list-item.component.ts # Item responsive
β β
β βββ note-view/
β β βββ app-markdown-viewer.component.ts # Viewer responsive
β β βββ app-toc-panel.component.ts # ToC desktop fixe
β β βββ app-toc-overlay.component.ts # ToC mobile overlay
β β
β βββ bottom-nav/ [NEW]
β β βββ app-bottom-navigation.component.ts # Navigation mobile[40]
β β βββ app-tab-navigation.component.ts # Navigation tablet
β β
β βββ mobile-content/ [NEW]
β βββ app-mobile-content.component.ts # Container mobile
β βββ app-tab-content.component.ts # Container tablet
β
βββ shared/
β βββ services/
β β βββ ui-mode.service.ts # Toggle UI management
β β βββ responsive.service.ts # Breakpoint detection[25]
β β βββ mobile-nav.service.ts # Tab/drawer state mobile
β β
β βββ directives/
β β βββ swipe-nav.directive.ts # Gesture detection[41]
β β βββ lazy-load.directive.ts # Image lazy loading
β β
β βββ components/
β βββ skeleton/
β β βββ app-skeleton.component.ts # Loading states
β βββ filter-chip/
β βββ app-filter-chip.component.ts # Chips rΓ©utilisables[1]
β
βββ styles/
βββ tokens.scss # Variables Nimbus
βββ responsive.scss # Utilitaires responsive[21]
βββ mobile.scss # Styles mobile-specific
βββ animations.scss # Transitions smooth
```
***
## β
Critères d'Acceptation
### β
Desktop (β₯1024px)
- Layout 3 colonnes (sidebar fixe/resizable, liste, page+ToC)[1][2]
- Changement dossier/tag/tri reflΓ©tΓ© en URL
- 1000+ items fluide (60fps virtual scroll)
- ToC synchronisΓ© + repliable cΓ΄tΓ© droit
- Navigation clavier-seule possible partout
### β
Mobile (<768px)
- Drawer sidebar (80vw max) avec backdrop[16][15]
- Bottom nav (4 onglets) sticky[12][11]
- Search bar compact (menu + search + filters)
- List items optimisΓ©s (titre + date + excerpt)
- Markdown full-screen avec ToC overlay
- Touch targets β₯ 44x44px[6]
- Swipe navigation entre onglets[13]
### β
Tablet (768-1023px)
- Navigation par 3 onglets principaux
- Contenu full-width par tab
- Drawer optionnel selon besoin
- Bottom navigation hybride
### β
Feature Flag
- Toggle UI visible dans navbar
- Γtat persistΓ© (localStorage)
- Pas de perte d'Γ©tat lors du switch
- Legacy UI reste intacte et fonctionnelle
### β
Performance & AccessibilitΓ©
- TTI < 2.5s cold start
- Scroll 60fps sur 1000+ items
- Lighthouse Mobile β₯ 85 perf, β₯ 95 a11y[8][7]
- WCAG 2.1 AA sur tous les breakpoints
- Keyboard navigation complète (Tab, Arrow, Enter)
- Focus visible partout, zoom β€ 200% sans scroll horizontal
***
## π Scripts & Commandes
```bash
# Dev complet (Nimbus activΓ© par dΓ©faut)
npm run dev
# Build production avec optimisations mobile
npm run build:mobile
# Tests responsifs (plusieurs breakpoints)
npm run test:responsive
# Lighthouse audit mobile
npm run audit:lighthouse:mobile
# Feature flag override
NIMBUS_UI=false npm run dev # Force legacy UI
NIMBUS_UI=true npm run dev # Force Nimbus UI
# Screenshots responsive pour tests visuels
npm run screenshot:all-breakpoints
```
***
## π Documentation Γ Produire
1. **README_UI_RESPONSIVE.md** : Architecture, breakpoints, captures d'Γ©cran 3 formats
2. **MOBILE_GUIDE.md** : Navigation onglets, gestures, patterns drawer
3. **RESPONSIVE_CHECKLIST.md** : Tests par breakpoint, device testing
4. **DEPLOYMENT_PROGRESSIVE.md** : Feature flag pour migration douce
5. **PERFORMANCE_MOBILE.md** : Optimisations, lazy loading, metrics
***
## π‘ Notes Cruciales
- **Mobile First Absolu** : DΓ©velopper mobile d'abord, puis enrichir tablet/desktop[5][6]
- **Zero Regression** : L'interface legacy reste 100% intacte et fonctionnelle
- **Γtat Persistant** : Toggle UI, filtres actifs, tab mobile via localStorage
- **Performance** : Virtual scroll adaptΓ© mobile, lazy-load images, skeleton loaders[7]
- **Accessibility** : 44px touch targets, ARIA labels complets, keyboard nav[6]
- **Testing** : Visual regression sur 3 breakpoints clΓ©s (375px/768px/1440px)
- **Gestures** : Swipe navigation fluide, long-press menus, pull-to-refresh optionnel[14][13]
***
**Exécute maintenant ce plan complet** : crée tous les composants responsifs, implémente les services de state management, configure les breakpoints Tailwind mobile-first, branche la navigation tactile avec gestures, et livre le MR conforme aux critères ci-dessus avec toggle UI et compatibilité 100% Desktop/Tablet/Mobile.[5][13]
[1](https://nimbuslearning.com/team_member/aaron-lee/)
[2](https://thefusebase.com/guides/android-ios/mobile-apps/)
[3](https://thefusebase.com/guides/getting-started/structure-in-nimbus-note/)
[4](https://blog.angular-university.io/angular-responsive-design/)
[5](https://tailwindcss.com/docs/responsive-design)
[6](https://dev.to/hitesh_developer/20-tips-for-designing-mobile-first-with-tailwind-css-36km)
[7](https://www.youtube.com/watch?v=S2eyA3zk0BQ)
[8](https://www.linkedin.com/pulse/designing-responsive-uis-angular-mobile-first-adaptive-yeturi-sx1wc)
[9](https://material.angular.dev/cdk/layout/overview)
[10](https://www.thisdot.co/blog/how-to-manage-breakpoints-using-breakpointobserver-in-angular)
[11](https://m2.material.io/components/bottom-navigation)
[12](https://www.infragistics.com/products/ignite-ui-angular/angular/components/tabbar)
[13](https://stackoverflow.com/questions/42592156/what-is-the-best-way-to-implement-swipe-navigation-in-angular-2)
[14](https://js.devexpress.com/Angular/Documentation/Guide/UI_Components/List/End-User_Interaction/Touch-Screen_Gestures/)
[15](https://material.angular.dev/components/sidenav/overview)
[16](https://dev.to/davidihl/how-to-create-a-responsive-sidebar-and-mini-navigation-with-material-angular-o5l)
[17](https://smart-interface-design-patterns.com)
[18](https://www.reddit.com/r/angular/comments/1gjjkpt/responsive_design_best_practice/)
[19](https://smashingconf.com/online-workshops/workshops/interface-design-course-vitaly-friedman-spring/)
[20](https://www.youtube.com/watch?v=svetVZJewDk)
[21](https://blog.pixelfreestudio.com/how-to-implement-mobile-first-design-in-angular/)
[22](https://devoxsoftware.com/blog/improve-engagement-of-your-saas-with-ux-ui-design-best-practices/)
[23](https://appsumo.com/products/fusebase/questions/pavel-1430723/)
[24](https://thefusebase.com/note/)
[25](https://www.youtube.com/watch?v=bGoemPZfQzk)
[26](https://angular.love/responsive-angular-components)
[27](https://www.thedesignership.com/blog/the-ultimate-guide-to-ux-ui-design-in-2024)
[28](https://fivetaco.com/products/fusebase)
[29](https://help.luware.com/best-practices-category/best-practices-designing-messages-and-adaptive-cards-in-ms-teams)
[30](https://www.reddit.com/r/FlutterDev/comments/1alapqu/should_i_separate_the_codebase_for_desktop_and/)
[31](https://stackoverflow.com/questions/65782044/without-media-queries-how-to-achieve-3-column-desktop-to-1-column-mobile-layout)
[32](https://m2.material.io/design/layout/responsive-layout-grid.html)
[33](https://tailwindcss.com/plus/templates/pocket)
[34](https://www.w3schools.com/howto/howto_css_three_columns.asp)
[35](https://stackoverflow.com/questions/78141479/how-to-change-desktop-first-design-code-to-mobile-first-with-tailwind-css-respo)
[36](https://www.linkedin.com/posts/angular-material-dev_angular-angularmaterial-webdevelopment-activity-7354526847910445056-jqNb)
[37](https://stackoverflow.com/questions/72945583/breakpointobserver-angular)
[38](https://www.reddit.com/r/Frontend/comments/1fevx2e/anyone_switched_from_desktop_first_to_mobile/)
[39](https://teamtreehouse.com/community/three-column-layout-that-is-responsive)
[40](https://www.youtube.com/watch?v=I13uAoOGU_4)
[41](https://dev.to/slyskillet/mobile-first-design-with-tailwind-css-3phd)
[42](https://www.telerik.com/kendo-angular-ui/components/grid/styling-and-appearance/responsive-design)
[43](https://www.digitalocean.com/community/tutorials/angular-breakpoints-angular-cdk)
[44](https://www.bootstrapdash.com/blog/tailwind-responsive-design-tips)
[45](https://www.reddit.com/r/Angular2/comments/w0co1t/how_to_implement_gestures_swipe_left_slider_in_an/)
[46](https://www.youtube.com/watch?v=4CYuOiRHHA8)
[47](https://www.telerik.com/kendo-angular-ui/bottomnavigation)
[48](https://developer.mozilla.org/en-US/docs/Web/API/Touch_events)
[49](https://stackoverflow.com/questions/57885511/angular-material-nav-sidebar-only-shows-on-responsive-resizing)
[50](https://stackoverflow.com/questions/50445545/create-mobile-bottom-navigation-with-angular-material)
[51](https://material.angular.dev/components/sidenav)
[52](https://material.angular.dev/components/tabs/overview)
[53](https://30dayscoding.com/blog/angular-animations-and-gesture-support)
[54](https://stackoverflow.com/questions/63642803/angular-material-sidenav-rail-mode-hidden-on-mobile)
[55](https://demo.mobiscroll.com/angular/navigation/bottom-navigation)
[56](https://angular.love/gestures-in-an-angular-application)
[57](https://stackblitz.com/edit/angular-closing-side-nav-in-mobile?file=app%2Fsidenav-responsive-example.html)
[58](https://www.reddit.com/r/ionic/comments/1jwmsnr/iontabbar_and_transparent_device_navbar/)