@elsahafy/ux-mcp-server 2.0.1 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,347 @@
1
+ {
2
+ "name": "Angular Patterns & Best Practices",
3
+ "description": "Comprehensive guide to Angular component patterns, services, RxJS, state management, and performance optimization",
4
+ "version_note": "Patterns focus on modern Angular (14+) with standalone components",
5
+ "architecture": {
6
+ "modules_vs_standalone": {
7
+ "ngmodules": {
8
+ "description": "Traditional Angular modules",
9
+ "use_when": "Legacy applications",
10
+ "example": "@NgModule({\n declarations": [AppComponent],\n imports: [BrowserModule],\n providers: [],\n bootstrap: [AppComponent]\n})\nexport class AppModule { }"
11
+ },
12
+ "standalone": {
13
+ "description": "Modern approach (Angular 14+)",
14
+ "recommended": true,
15
+ "example": "@Component({\n selector: 'app-root',\n standalone: true,\n imports: [CommonModule, RouterModule],\n template: `<h1>Hello</h1>`\n})\nexport class AppComponent { }",
16
+ "benefits": ["Simpler mental model", "Better tree-shaking", "Easier testing", "Less boilerplate"]
17
+ }
18
+ },
19
+ "feature_structure": {
20
+ "description": "Organize by feature, not by type",
21
+ "recommended_structure": "src/\n app/\n features/\n users/\n components/\n services/\n models/\n users.routes.ts\n products/\n components/\n services/\n models/\n products.routes.ts\n shared/\n components/\n directives/\n pipes/\n core/\n services/\n guards/\n interceptors/",
22
+ "benefits": ["Easier to navigate", "Clear boundaries", "Better modularity"]
23
+ }
24
+ },
25
+ "components": {
26
+ "component_types": {
27
+ "smart_container": {
28
+ "description": "Manages state and business logic",
29
+ "responsibilities": ["Fetch data", "Manage state", "Pass data to presentational components"],
30
+ "example": "@Component({\n selector: 'app-user-list',\n template: `\n <app-user-card\n *ngFor='let user of users$ | async'\n [user]='user'\n (delete)='onDelete($event)'\n />\n `\n})\nexport class UserListComponent {\n users$ = this.userService.getUsers()\n \n constructor(private userService: UserService) {}\n \n onDelete(id: number) {\n this.userService.deleteUser(id).subscribe()\n }\n}"
31
+ },
32
+ "presentational_dumb": {
33
+ "description": "Pure display components",
34
+ "responsibilities": ["Display data", "Emit events"],
35
+ "example": "@Component({\n selector: 'app-user-card',\n template: `\n <div class='card'>\n <h3>{{ user.name }}</h3>\n <button (click)='delete.emit(user.id)'>Delete</button>\n </div>\n `\n})\nexport class UserCardComponent {\n @Input() user!: User\n @Output() delete = new EventEmitter<number>()\n}",
36
+ "benefits": ["Reusable", "Testable", "No dependencies"]
37
+ }
38
+ },
39
+ "lifecycle_hooks": {
40
+ "ngOnInit": {
41
+ "when": "After first ngOnChanges",
42
+ "use": "Component initialization, fetch data",
43
+ "example": "ngOnInit() {\n this.loadData()\n}"
44
+ },
45
+ "ngOnChanges": {
46
+ "when": "When @Input() properties change",
47
+ "use": "React to input changes",
48
+ "example": "ngOnChanges(changes: SimpleChanges) {\n if (changes['userId']) {\n this.loadUser()\n }\n}"
49
+ },
50
+ "ngOnDestroy": {
51
+ "when": "Before component is destroyed",
52
+ "use": "Cleanup, unsubscribe",
53
+ "example": "ngOnDestroy() {\n this.subscription.unsubscribe()\n}"
54
+ },
55
+ "ngAfterViewInit": {
56
+ "when": "After view initialization",
57
+ "use": "Access ViewChild elements",
58
+ "example": "ngAfterViewInit() {\n this.chart.nativeElement.render()\n}"
59
+ }
60
+ },
61
+ "inputs_outputs": {
62
+ "input": {
63
+ "description": "Pass data from parent to child",
64
+ "example": "@Input() user!: User\n@Input() count = 0 // with default",
65
+ "required": "@Input({ required: true }) userId!: number",
66
+ "alias": "@Input('userName') name!: string",
67
+ "transform": "@Input({ transform: booleanAttribute }) disabled = false"
68
+ },
69
+ "output": {
70
+ "description": "Emit events to parent",
71
+ "example": "@Output() delete = new EventEmitter<number>()\n\nonDelete() {\n this.delete.emit(this.user.id)\n}",
72
+ "naming": "Use present tense: 'delete', not 'deleted' or 'onDelete'"
73
+ },
74
+ "two_way_binding": {
75
+ "pattern": "[(ngModel)] syntax",
76
+ "example": "// Child component:\n@Input() value!: string\n@Output() valueChange = new EventEmitter<string>()\n\nonInput(newValue: string) {\n this.value = newValue\n this.valueChange.emit(newValue)\n}\n\n// Parent:\n<app-input [(value)]='text' />"
77
+ }
78
+ },
79
+ "content_projection": {
80
+ "single_slot": {
81
+ "example": "// Component:\n<div class='card'>\n <ng-content /></div>\n</div>\n\n// Usage:\n<app-card>\n <p>Content goes here</p>\n</app-card>"
82
+ },
83
+ "multiple_slots": {
84
+ "example": "// Component:\n<div class='card'>\n <header>\n <ng-content select='[header]' />\n </header>\n <main>\n <ng-content />\n </main>\n <footer>\n <ng-content select='[footer]' />\n </footer>\n</div>\n\n// Usage:\n<app-card>\n <h2 header>Title</h2>\n <p>Content</p>\n <button footer>Action</button>\n</app-card>"
85
+ },
86
+ "ng_template": {
87
+ "example": "// Component:\n@Input() itemTemplate!: TemplateRef<any>\n\n<ng-container *ngFor='let item of items'>\n <ng-container *ngTemplateOutlet='itemTemplate; context: { $implicit: item }' />\n</ng-container>\n\n// Usage:\n<app-list [itemTemplate]='itemTpl'>\n <ng-template #itemTpl let-item>\n <div>{{ item.name }}</div>\n </ng-template>\n</app-list>"
88
+ }
89
+ },
90
+ "change_detection": {
91
+ "default": {
92
+ "description": "Checks entire component tree on every event",
93
+ "performance": "Can be slow for large apps"
94
+ },
95
+ "onpush": {
96
+ "description": "Only checks when inputs change or events occur",
97
+ "recommended": true,
98
+ "example": "@Component({\n changeDetection: ChangeDetectionStrategy.OnPush\n})",
99
+ "triggers": ["@Input() reference changes", "Events from template", "Async pipe emits"],
100
+ "benefits": ["Better performance", "Predictable updates"]
101
+ },
102
+ "manual": {
103
+ "description": "Manually trigger change detection",
104
+ "example": "constructor(private cdr: ChangeDetectorRef) {}\n\nupdate() {\n // Manual update\n this.cdr.markForCheck()\n}",
105
+ "when": "OnPush + manual updates needed"
106
+ }
107
+ }
108
+ },
109
+ "services_di": {
110
+ "services": {
111
+ "description": "Business logic, data fetching, state management",
112
+ "example": "@Injectable({ providedIn: 'root' })\nexport class UserService {\n private http = inject(HttpClient)\n \n getUsers() {\n return this.http.get<User[]>('/api/users')\n }\n}",
113
+ "provided_in": {
114
+ "root": "Singleton across app (recommended for most services)",
115
+ "any": "Lazy loaded with feature module",
116
+ "platform": "Shared across apps in multi-app setup",
117
+ "component": "New instance per component instance"
118
+ }
119
+ },
120
+ "dependency_injection": {
121
+ "constructor_injection": {
122
+ "legacy": "constructor(private service: MyService) {}",
123
+ "modern": "private service = inject(MyService)",
124
+ "recommended": "Use inject() function"
125
+ },
126
+ "injection_tokens": {
127
+ "description": "Provide non-class dependencies",
128
+ "example": "export const API_URL = new InjectionToken<string>('api.url')\n\n// Provide:\nproviders: [{ provide: API_URL, useValue: 'https://api.example.com' }]\n\n// Inject:\nprivate apiUrl = inject(API_URL)"
129
+ },
130
+ "multi_providers": {
131
+ "description": "Provide multiple values for same token",
132
+ "example": "export const INTERCEPTORS = new InjectionToken<HttpInterceptor[]>('http.interceptors')\n\nproviders: [\n { provide: INTERCEPTORS, useClass: AuthInterceptor, multi: true },\n { provide: INTERCEPTORS, useClass: LoggingInterceptor, multi: true }\n]"
133
+ }
134
+ }
135
+ },
136
+ "rxjs_patterns": {
137
+ "observables": {
138
+ "description": "Asynchronous data streams",
139
+ "creation": "const numbers$ = of(1, 2, 3)\nconst timer$ = interval(1000)",
140
+ "subscription": "numbers$.subscribe({\n next: (value) => console.log(value),\n error: (err) => console.error(err),\n complete: () => console.log('Done')\n})"
141
+ },
142
+ "operators": {
143
+ "map": {
144
+ "use": "Transform emitted values",
145
+ "example": "users$.pipe(\n map(users => users.filter(u => u.active))\n)"
146
+ },
147
+ "switchMap": {
148
+ "use": "Cancel previous inner observable",
149
+ "example": "searchTerm$.pipe(\n switchMap(term => this.http.get(`/search?q=${term}`))\n)",
150
+ "when": "HTTP requests, don't want to wait for previous"
151
+ },
152
+ "mergeMap": {
153
+ "use": "Don't cancel previous (parallel)",
154
+ "example": "ids$.pipe(\n mergeMap(id => this.http.get(`/users/${id}`))\n)",
155
+ "when": "Independent requests that should run in parallel"
156
+ },
157
+ "concatMap": {
158
+ "use": "Wait for previous to complete (sequential)",
159
+ "example": "requests$.pipe(\n concatMap(req => this.http.post('/api', req))\n)",
160
+ "when": "Order matters, wait for each to finish"
161
+ },
162
+ "debounceTime": {
163
+ "use": "Wait for pause in emissions",
164
+ "example": "searchInput$.pipe(\n debounceTime(300),\n switchMap(term => this.search(term))\n)",
165
+ "when": "Search input, frequent events"
166
+ },
167
+ "distinctUntilChanged": {
168
+ "use": "Emit only when value changes",
169
+ "example": "value$.pipe(\n distinctUntilChanged()\n)"
170
+ },
171
+ "catchError": {
172
+ "use": "Handle errors",
173
+ "example": "this.http.get('/api').pipe(\n catchError(error => of({ error: true }))\n)"
174
+ },
175
+ "shareReplay": {
176
+ "use": "Share and replay last N values",
177
+ "example": "users$ = this.http.get<User[]>('/users').pipe(\n shareReplay(1)\n)",
178
+ "when": "Cache HTTP results, multiple subscribers"
179
+ }
180
+ },
181
+ "subjects": {
182
+ "subject": "Basic subject (no initial value)",
183
+ "behaviorSubject": "Has current value, emits last value to new subscribers",
184
+ "replaySubject": "Replays N last values to new subscribers",
185
+ "example": "private userSubject = new BehaviorSubject<User | null>(null)\nuser$ = this.userSubject.asObservable()\n\nsetUser(user: User) {\n this.userSubject.next(user)\n}"
186
+ },
187
+ "async_pipe": {
188
+ "description": "Subscribe in template, auto-unsubscribe",
189
+ "example": "<div *ngIf='user$ | async as user'>\n {{ user.name }}\n</div>",
190
+ "benefits": ["Auto-unsubscribe", "Works with OnPush", "Less boilerplate"],
191
+ "recommended": "Use instead of manual subscribe in components"
192
+ },
193
+ "takeUntil_pattern": {
194
+ "description": "Unsubscribe on component destroy",
195
+ "example": "private destroy$ = new Subject<void>()\n\nngOnInit() {\n this.data$.pipe(\n takeUntil(this.destroy$)\n ).subscribe(data => this.data = data)\n}\n\nngOnDestroy() {\n this.destroy$.next()\n this.destroy$.complete()\n}",
196
+ "better": "Use async pipe instead to avoid manual unsubscribe"
197
+ }
198
+ },
199
+ "forms": {
200
+ "reactive_forms": {
201
+ "description": "Model-driven forms (recommended)",
202
+ "example": "form = new FormGroup({\n name: new FormControl('', [Validators.required]),\n email: new FormControl('', [Validators.required, Validators.email])\n})\n\n<form [formGroup]='form' (ngSubmit)='onSubmit()'>\n <input formControlName='name'>\n <input formControlName='email'>\n <button type='submit' [disabled]='form.invalid'>Submit</button>\n</form>",
203
+ "benefits": ["Type-safe", "Testable", "Programmatic control"],
204
+ "validation": "form.get('email')?.hasError('required')"
205
+ },
206
+ "template_driven": {
207
+ "description": "Template-based forms",
208
+ "example": "<form #f='ngForm' (ngSubmit)='onSubmit(f)'>\n <input name='name' ngModel required>\n <button [disabled]='f.invalid'>Submit</button>\n</form>",
209
+ "use_when": "Simple forms only",
210
+ "avoid": "Complex forms (use reactive instead)"
211
+ },
212
+ "formbuilder": {
213
+ "description": "Helper for creating reactive forms",
214
+ "example": "private fb = inject(FormBuilder)\n\nform = this.fb.group({\n name: ['', Validators.required],\n email: ['', [Validators.required, Validators.email]],\n address: this.fb.group({\n street: [''],\n city: ['']\n })\n})",
215
+ "benefits": "Less verbose than new FormGroup/FormControl"
216
+ },
217
+ "custom_validators": {
218
+ "example": "function emailDomain(domain: string): ValidatorFn {\n return (control: AbstractControl): ValidationErrors | null => {\n const email = control.value\n if (email && !email.endsWith(`@${domain}`)) {\n return { emailDomain: { requiredDomain: domain } }\n }\n return null\n }\n}\n\nform = this.fb.group({\n email: ['', [Validators.email, emailDomain('company.com')]]\n})"
219
+ }
220
+ },
221
+ "routing": {
222
+ "setup": {
223
+ "legacy": "RouterModule.forRoot(routes)",
224
+ "standalone": "provideRouter(routes)",
225
+ "example": "const routes: Routes = [\n { path: '', component: HomeComponent },\n { path: 'users/:id', component: UserComponent },\n { path: '**', component: NotFoundComponent }\n]"
226
+ },
227
+ "lazy_loading": {
228
+ "description": "Load routes on demand",
229
+ "example": "{\n path: 'admin',\n loadComponent: () => import('./admin/admin.component').then(m => m.AdminComponent)\n}",
230
+ "benefits": ["Smaller initial bundle", "Faster startup"]
231
+ },
232
+ "route_params": {
233
+ "example": "private route = inject(ActivatedRoute)\n\nuserId$ = this.route.paramMap.pipe(\n map(params => params.get('id'))\n)"
234
+ },
235
+ "route_guards": {
236
+ "canActivate": {
237
+ "description": "Protect routes",
238
+ "example": "export const authGuard: CanActivateFn = () => {\n const authService = inject(AuthService)\n const router = inject(Router)\n \n if (authService.isAuthenticated()) {\n return true\n }\n return router.createUrlTree(['/login'])\n}\n\n// In routes:\n{ path: 'admin', component: AdminComponent, canActivate: [authGuard] }"
239
+ },
240
+ "canDeactivate": "Prevent navigation away (unsaved changes)",
241
+ "resolve": "Pre-fetch data before route activation"
242
+ }
243
+ },
244
+ "state_management": {
245
+ "local_state": {
246
+ "description": "Component-level state",
247
+ "signals": "user = signal<User | null>(null)\n\nsetUser(user: User) {\n this.user.set(user)\n}\n\n// In template:\n{{ user()?.name }}"
248
+ },
249
+ "signals": {
250
+ "description": "Modern reactive primitive (Angular 16+)",
251
+ "writable": "count = signal(0)\ncount.set(5)\ncount.update(n => n + 1)",
252
+ "computed": "doubled = computed(() => this.count() * 2)",
253
+ "effect": "effect(() => {\n console.log('Count:', this.count())\n})",
254
+ "benefits": ["Simpler than RxJS for simple state", "Better change detection integration"]
255
+ },
256
+ "ngrx": {
257
+ "description": "Redux-style state management",
258
+ "when": "Complex state, time-travel debugging needed",
259
+ "store": "State container",
260
+ "actions": "Events that describe state changes",
261
+ "reducers": "Pure functions that update state",
262
+ "effects": "Side effects (HTTP, etc.)",
263
+ "example": "// Action\nexport const loadUsers = createAction('[User] Load Users')\n\n// Reducer\nconst userReducer = createReducer(\n initialState,\n on(loadUsers, state => ({ ...state, loading: true }))\n)\n\n// Component\nusers$ = this.store.select(selectAllUsers)\n\nloadUsers() {\n this.store.dispatch(loadUsers())\n}"
264
+ },
265
+ "component_store": {
266
+ "description": "Local state management with RxJS",
267
+ "use_when": "Component-level complex state",
268
+ "simpler_than": "NgRx for local state"
269
+ },
270
+ "services_with_signals": {
271
+ "description": "Lightweight state management",
272
+ "example": "@Injectable({ providedIn: 'root' })\nexport class UserStore {\n private _users = signal<User[]>([])\n users = this._users.asReadonly()\n \n addUser(user: User) {\n this._users.update(users => [...users, user])\n }\n}",
273
+ "recommended": "Start here before adding NgRx"
274
+ }
275
+ },
276
+ "performance": {
277
+ "trackBy": {
278
+ "description": "Improve ngFor performance",
279
+ "example": "<div *ngFor='let item of items; trackBy: trackById'>\n {{ item.name }}\n</div>\n\ntrackById(index: number, item: Item) {\n return item.id\n}",
280
+ "when": "Large lists, frequent updates"
281
+ },
282
+ "lazy_loading": "Split app into lazy-loaded modules/components",
283
+ "virtual_scrolling": {
284
+ "description": "Render only visible items",
285
+ "example": "<cdk-virtual-scroll-viewport itemSize='50' class='viewport'>\n <div *cdkVirtualFor='let item of items'>\n {{ item }}\n </div>\n</cdk-virtual-scroll-viewport>",
286
+ "when": "Long lists (100+ items)"
287
+ },
288
+ "pure_pipes": {
289
+ "description": "Cache pipe results",
290
+ "example": "@Pipe({ name: 'expensive', pure: true })\nexport class ExpensivePipe implements PipeTransform {\n transform(value: any): any {\n // Expensive computation\n }\n}",
291
+ "default": "Pipes are pure by default"
292
+ }
293
+ },
294
+ "testing": {
295
+ "unit_tests": {
296
+ "example": "describe('UserComponent', () => {\n let component: UserComponent\n let fixture: ComponentFixture<UserComponent>\n \n beforeEach(() => {\n TestBed.configureTestingModule({\n imports: [UserComponent]\n })\n fixture = TestBed.createComponent(UserComponent)\n component = fixture.componentInstance\n })\n \n it('should display user name', () => {\n component.user = { id: 1, name: 'John' }\n fixture.detectChanges()\n const compiled = fixture.nativeElement\n expect(compiled.querySelector('h1').textContent).toContain('John')\n })\n})"
297
+ },
298
+ "service_testing": {
299
+ "example": "describe('UserService', () => {\n let service: UserService\n let httpMock: HttpTestingController\n \n beforeEach(() => {\n TestBed.configureTestingModule({\n imports: [HttpClientTestingModule],\n providers: [UserService]\n })\n service = TestBed.inject(UserService)\n httpMock = TestBed.inject(HttpTestingController)\n })\n \n it('should fetch users', () => {\n const mockUsers = [{ id: 1, name: 'John' }]\n \n service.getUsers().subscribe(users => {\n expect(users).toEqual(mockUsers)\n })\n \n const req = httpMock.expectOne('/api/users')\n expect(req.request.method).toBe('GET')\n req.flush(mockUsers)\n })\n})"
300
+ }
301
+ },
302
+ "best_practices": [
303
+ "Use standalone components for new projects",
304
+ "Use OnPush change detection strategy",
305
+ "Prefer inject() over constructor injection",
306
+ "Use async pipe instead of manual subscribe",
307
+ "Use signals for simple reactive state (Angular 16+)",
308
+ "Use trackBy with ngFor for performance",
309
+ "Lazy load routes and features",
310
+ "Keep components small (< 300 lines)",
311
+ "Use reactive forms (not template-driven)",
312
+ "Use services for business logic",
313
+ "Follow single responsibility principle",
314
+ "Use TypeScript strict mode",
315
+ "Use pure pipes",
316
+ "Avoid DOM manipulation (use Renderer2 if needed)",
317
+ "Test components and services",
318
+ "Use virtual scrolling for long lists",
319
+ "Use FormBuilder for reactive forms",
320
+ "Unsubscribe from observables (or use async pipe)",
321
+ "Use providedIn: 'root' for services",
322
+ "Follow Angular style guide"
323
+ ],
324
+ "anti_patterns": [
325
+ "Subscribing in templates",
326
+ "Not unsubscribing from observables",
327
+ "Using default change detection for all components",
328
+ "Manipulating DOM directly (use Renderer2)",
329
+ "Large components (> 500 lines)",
330
+ "Business logic in components (use services)",
331
+ "Not using trackBy with ngFor",
332
+ "Template-driven forms for complex forms",
333
+ "Circular dependencies between modules",
334
+ "Not lazy loading features",
335
+ "Using any type everywhere",
336
+ "Not testing",
337
+ "Ignoring Angular CLI warnings",
338
+ "Nested subscriptions (use switchMap/mergeMap)",
339
+ "Not using strict mode"
340
+ ],
341
+ "ecosystem": {
342
+ "ui_libraries": ["Angular Material", "PrimeNG", "Ng-Bootstrap", "Ant Design Angular"],
343
+ "state_management": ["NgRx", "NgRx Component Store", "Akita", "Elf"],
344
+ "testing": ["Jasmine + Karma", "Jest", "Cypress", "Playwright"],
345
+ "build_tools": ["Angular CLI", "Nx (monorepos)"]
346
+ }
347
+ }
@@ -0,0 +1,139 @@
1
+ {
2
+ "name": "AR/VR Interface Design",
3
+ "description": "Design principles and patterns for Augmented Reality (AR) and Virtual Reality (VR) interfaces",
4
+ "types": {
5
+ "vr": {
6
+ "description": "Fully immersive, replaces real world",
7
+ "devices": ["Meta Quest", "PlayStation VR", "HTC Vive", "Apple Vision Pro"],
8
+ "use_cases": ["Gaming", "Training simulations", "Virtual meetings", "3D modeling"]
9
+ },
10
+ "ar": {
11
+ "description": "Overlays digital content on real world",
12
+ "devices": ["Smartphones (AR Kit, AR Core)", "AR glasses (HoloLens, Magic Leap)", "Apple Vision Pro (mixed)"],
13
+ "use_cases": ["Navigation", "Product visualization", "Maintenance/repair", "Education"]
14
+ },
15
+ "mixed_reality": {
16
+ "description": "Blend of VR and AR",
17
+ "example": "Apple Vision Pro, HoloLens",
18
+ "capability": "Interact with virtual and real objects"
19
+ }
20
+ },
21
+ "vr_design_principles": {
22
+ "comfort": {
23
+ "motion_sickness": {
24
+ "causes": ["Frame rate < 90fps", "Latency > 20ms", "Acceleration without user control"],
25
+ "mitigation": ["Maintain 90+ fps", "Teleportation movement", "Fixed reference points", "Vignetting during motion"]
26
+ },
27
+ "field_of_view": "110° minimum to feel natural",
28
+ "ergonomics": "Avoid prolonged head tilting (up/down), position UI at eye level"
29
+ },
30
+ "presence": {
31
+ "description": "Feeling of 'being there'",
32
+ "techniques": ["Spatial audio (3D sound)", "Realistic scale (1:1 mapping)", "Hand tracking", "Shadows and lighting"]
33
+ },
34
+ "interaction": {
35
+ "gaze": {
36
+ "use": "Selection (dwell timer or blink)",
37
+ "issue": "Eye strain, 'Midas touch' (everything activates)"
38
+ },
39
+ "controllers": {
40
+ "types": ["6DOF controllers (position + rotation)", "Hand tracking (gesture-based)"],
41
+ "best_practice": "Provide haptic feedback, clear button mappings"
42
+ },
43
+ "voice": "Hands-free interaction, ideal for menus and commands"
44
+ },
45
+ "spatial_ui": {
46
+ "placement": "1.5-3 meters from user, at eye level",
47
+ "size": "Large enough to read (min 36pt font equivalent)",
48
+ "canvas_vs_world_space": {
49
+ "canvas": "Follows user (HUD, menus)",
50
+ "world": "Fixed in 3D space (signs, objects)"
51
+ }
52
+ }
53
+ },
54
+ "ar_design_principles": {
55
+ "context_awareness": {
56
+ "description": "AR must understand real-world context",
57
+ "techniques": ["Plane detection (floor, walls)", "Object recognition", "Lighting estimation"]
58
+ },
59
+ "occlusion": {
60
+ "description": "Virtual objects behind real objects should be hidden",
61
+ "importance": "Critical for realism"
62
+ },
63
+ "anchoring": {
64
+ "description": "Virtual objects stay fixed to real-world location",
65
+ "methods": ["Surface anchors (table, floor)", "Image anchors (QR code, poster)"]
66
+ },
67
+ "ui_placement": {
68
+ "avoid": "Floating UI in center of view (blocks real world)",
69
+ "prefer": "Edge-anchored or world-anchored UI"
70
+ }
71
+ },
72
+ "interaction_patterns": {
73
+ "vr_locomotion": {
74
+ "teleportation": { "pros": ["Comfortable", "No motion sickness"], "cons": ["Less immersive"] },
75
+ "smooth_locomotion": { "pros": ["Immersive"], "cons": ["Motion sickness risk"], "mitigation": "Vignetting, slow speed" },
76
+ "room_scale": { "description": "Walk in real space", "limitation": "Requires large physical space" }
77
+ },
78
+ "selection": {
79
+ "ray_casting": "Point and click with controller (like laser pointer)",
80
+ "direct_touch": "Reach and grab (more natural, limited by arm reach)",
81
+ "gaze_dwell": "Look at object for 2 seconds to select"
82
+ },
83
+ "manipulation": {
84
+ "grab_and_move": "Natural for VR, use hand tracking or controllers",
85
+ "scale_rotate": "Pinch gestures (two-handed) or controller buttons"
86
+ }
87
+ },
88
+ "accessibility": {
89
+ "vr_limitations": {
90
+ "motion_sickness": "10-30% of users experience it",
91
+ "physical_disabilities": "May not be able to stand or move freely",
92
+ "visual_impairments": "Depth perception issues, low vision",
93
+ "mitigation": ["Seated experiences", "Teleportation movement", "Audio descriptions", "Adjustable text size"]
94
+ },
95
+ "ar_limitations": {
96
+ "outdoor_usability": "Bright sunlight makes AR hard to see",
97
+ "accessibility": "Blind users can't see AR overlays (provide audio alternative)"
98
+ }
99
+ },
100
+ "best_practices": [
101
+ "Maintain 90+ fps in VR (comfort)",
102
+ "Avoid user-triggered acceleration (motion sickness)",
103
+ "Use spatial audio for immersion",
104
+ "Position UI at eye level, 1.5-3m away",
105
+ "Provide multiple locomotion options (teleport, smooth)",
106
+ "Use haptic feedback for interactions",
107
+ "Test with diverse users (motion sensitivity varies)",
108
+ "Anchor AR content to real-world surfaces",
109
+ "Handle occlusion in AR",
110
+ "Provide tutorials for VR (controls aren't obvious)",
111
+ "Design for seated and standing experiences",
112
+ "Use large text (36pt+ equivalent)",
113
+ "Avoid small, precise interactions (hard in VR)",
114
+ "Limit session duration (VR fatigue after 30-60 min)"
115
+ ],
116
+ "anti_patterns": [
117
+ "< 90 fps (causes nausea)",
118
+ "High latency (> 20ms head tracking)",
119
+ "Acceleration without user control",
120
+ "Tiny text (unreadable in VR)",
121
+ "UI too close (< 1m, eye strain)",
122
+ "Ignoring motion sickness users",
123
+ "No tutorial (users lost in VR)",
124
+ "Requiring precise hand movements (hand tracking jitter)",
125
+ "Long VR sessions without breaks",
126
+ "AR UI blocking real world"
127
+ ],
128
+ "tools": {
129
+ "vr_development": ["Unity + XR Interaction Toolkit", "Unreal Engine + VR Template", "A-Frame (WebVR)"],
130
+ "ar_development": ["Unity + AR Foundation", "AR Kit (iOS)", "AR Core (Android)", "8th Wall (WebAR)"],
131
+ "design_tools": ["Figma + VR plugins", "Gravity Sketch (VR 3D modeling)", "Tilt Brush (VR painting)"]
132
+ },
133
+ "resources": [
134
+ "Oculus Design Guidelines",
135
+ "Google AR Design Guidelines",
136
+ "Apple Human Interface Guidelines - visionOS",
137
+ "Unity Learn - VR Best Practices"
138
+ ]
139
+ }