omgkit 2.1.0 → 2.2.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.
Files changed (56) hide show
  1. package/package.json +1 -1
  2. package/plugin/skills/SKILL_STANDARDS.md +743 -0
  3. package/plugin/skills/databases/mongodb/SKILL.md +797 -28
  4. package/plugin/skills/databases/postgresql/SKILL.md +494 -18
  5. package/plugin/skills/databases/prisma/SKILL.md +776 -30
  6. package/plugin/skills/databases/redis/SKILL.md +885 -25
  7. package/plugin/skills/devops/aws/SKILL.md +686 -28
  8. package/plugin/skills/devops/docker/SKILL.md +466 -18
  9. package/plugin/skills/devops/github-actions/SKILL.md +684 -29
  10. package/plugin/skills/devops/kubernetes/SKILL.md +621 -24
  11. package/plugin/skills/frameworks/django/SKILL.md +920 -20
  12. package/plugin/skills/frameworks/express/SKILL.md +1361 -35
  13. package/plugin/skills/frameworks/fastapi/SKILL.md +1260 -33
  14. package/plugin/skills/frameworks/laravel/SKILL.md +1244 -31
  15. package/plugin/skills/frameworks/nestjs/SKILL.md +1005 -26
  16. package/plugin/skills/frameworks/nextjs/SKILL.md +407 -44
  17. package/plugin/skills/frameworks/rails/SKILL.md +594 -28
  18. package/plugin/skills/frameworks/react/SKILL.md +1006 -32
  19. package/plugin/skills/frameworks/spring/SKILL.md +528 -35
  20. package/plugin/skills/frameworks/vue/SKILL.md +1296 -27
  21. package/plugin/skills/frontend/accessibility/SKILL.md +1108 -34
  22. package/plugin/skills/frontend/frontend-design/SKILL.md +1304 -26
  23. package/plugin/skills/frontend/responsive/SKILL.md +847 -21
  24. package/plugin/skills/frontend/shadcn-ui/SKILL.md +976 -38
  25. package/plugin/skills/frontend/tailwindcss/SKILL.md +831 -35
  26. package/plugin/skills/frontend/threejs/SKILL.md +1298 -29
  27. package/plugin/skills/languages/javascript/SKILL.md +935 -31
  28. package/plugin/skills/languages/python/SKILL.md +489 -25
  29. package/plugin/skills/languages/typescript/SKILL.md +379 -30
  30. package/plugin/skills/methodology/brainstorming/SKILL.md +597 -23
  31. package/plugin/skills/methodology/defense-in-depth/SKILL.md +832 -34
  32. package/plugin/skills/methodology/dispatching-parallel-agents/SKILL.md +665 -31
  33. package/plugin/skills/methodology/executing-plans/SKILL.md +556 -24
  34. package/plugin/skills/methodology/finishing-development-branch/SKILL.md +595 -25
  35. package/plugin/skills/methodology/problem-solving/SKILL.md +429 -61
  36. package/plugin/skills/methodology/receiving-code-review/SKILL.md +536 -24
  37. package/plugin/skills/methodology/requesting-code-review/SKILL.md +632 -21
  38. package/plugin/skills/methodology/root-cause-tracing/SKILL.md +641 -30
  39. package/plugin/skills/methodology/sequential-thinking/SKILL.md +262 -3
  40. package/plugin/skills/methodology/systematic-debugging/SKILL.md +571 -32
  41. package/plugin/skills/methodology/test-driven-development/SKILL.md +779 -24
  42. package/plugin/skills/methodology/testing-anti-patterns/SKILL.md +691 -29
  43. package/plugin/skills/methodology/token-optimization/SKILL.md +598 -29
  44. package/plugin/skills/methodology/verification-before-completion/SKILL.md +543 -22
  45. package/plugin/skills/methodology/writing-plans/SKILL.md +590 -18
  46. package/plugin/skills/omega/omega-architecture/SKILL.md +838 -39
  47. package/plugin/skills/omega/omega-coding/SKILL.md +636 -39
  48. package/plugin/skills/omega/omega-sprint/SKILL.md +855 -48
  49. package/plugin/skills/omega/omega-testing/SKILL.md +940 -41
  50. package/plugin/skills/omega/omega-thinking/SKILL.md +703 -50
  51. package/plugin/skills/security/better-auth/SKILL.md +1065 -28
  52. package/plugin/skills/security/oauth/SKILL.md +968 -31
  53. package/plugin/skills/security/owasp/SKILL.md +894 -33
  54. package/plugin/skills/testing/playwright/SKILL.md +764 -38
  55. package/plugin/skills/testing/pytest/SKILL.md +873 -36
  56. package/plugin/skills/testing/vitest/SKILL.md +980 -35
@@ -1,46 +1,872 @@
1
1
  ---
2
2
  name: responsive
3
- description: Responsive design. Use for mobile-first, adaptive layouts.
3
+ description: Responsive web design with mobile-first approach, fluid layouts, container queries, and cross-device testing
4
+ category: frontend
5
+ triggers:
6
+ - responsive
7
+ - responsive design
8
+ - mobile first
9
+ - adaptive layout
10
+ - breakpoints
11
+ - media queries
4
12
  ---
5
13
 
6
- # Responsive Design Skill
14
+ # Responsive Design
15
+
16
+ Enterprise-grade **responsive web design** following industry best practices. This skill covers mobile-first development, fluid layouts, container queries, responsive images, and cross-device optimization patterns used by top engineering teams.
17
+
18
+ ## Purpose
19
+
20
+ Build seamless cross-device experiences:
21
+
22
+ - Implement mobile-first design approach
23
+ - Create fluid, flexible layouts
24
+ - Use modern CSS layout techniques
25
+ - Optimize images for all devices
26
+ - Implement container queries
27
+ - Test across devices and viewports
28
+ - Handle touch and mouse interactions
29
+
30
+ ## Features
31
+
32
+ ### 1. Mobile-First Breakpoints
7
33
 
8
- ## Breakpoints
9
34
  ```css
10
- /* Mobile first */
11
- .element { /* Mobile styles */ }
35
+ /* Base styles (mobile-first) */
36
+ :root {
37
+ /* Breakpoint values */
38
+ --breakpoint-sm: 640px;
39
+ --breakpoint-md: 768px;
40
+ --breakpoint-lg: 1024px;
41
+ --breakpoint-xl: 1280px;
42
+ --breakpoint-2xl: 1536px;
43
+ }
44
+
45
+ /* Mobile base styles */
46
+ .container {
47
+ width: 100%;
48
+ padding-left: 1rem;
49
+ padding-right: 1rem;
50
+ margin-left: auto;
51
+ margin-right: auto;
52
+ }
53
+
54
+ /* Small devices (640px and up) */
55
+ @media (min-width: 640px) {
56
+ .container {
57
+ max-width: 640px;
58
+ padding-left: 1.5rem;
59
+ padding-right: 1.5rem;
60
+ }
61
+ }
62
+
63
+ /* Medium devices (768px and up) */
64
+ @media (min-width: 768px) {
65
+ .container {
66
+ max-width: 768px;
67
+ }
68
+ }
69
+
70
+ /* Large devices (1024px and up) */
71
+ @media (min-width: 1024px) {
72
+ .container {
73
+ max-width: 1024px;
74
+ padding-left: 2rem;
75
+ padding-right: 2rem;
76
+ }
77
+ }
12
78
 
13
- @media (min-width: 640px) { /* sm */ }
14
- @media (min-width: 768px) { /* md */ }
15
- @media (min-width: 1024px) { /* lg */ }
16
- @media (min-width: 1280px) { /* xl */ }
79
+ /* Extra large devices (1280px and up) */
80
+ @media (min-width: 1280px) {
81
+ .container {
82
+ max-width: 1280px;
83
+ }
84
+ }
85
+
86
+ /* 2XL devices (1536px and up) */
87
+ @media (min-width: 1536px) {
88
+ .container {
89
+ max-width: 1536px;
90
+ }
91
+ }
17
92
  ```
18
93
 
19
- ## Patterns
94
+ ```typescript
95
+ // hooks/useBreakpoint.ts
96
+ import { useState, useEffect } from "react";
97
+
98
+ const breakpoints = {
99
+ sm: 640,
100
+ md: 768,
101
+ lg: 1024,
102
+ xl: 1280,
103
+ "2xl": 1536,
104
+ } as const;
105
+
106
+ type Breakpoint = keyof typeof breakpoints;
107
+
108
+ export function useBreakpoint() {
109
+ const [breakpoint, setBreakpoint] = useState<Breakpoint | null>(null);
110
+ const [width, setWidth] = useState(0);
111
+
112
+ useEffect(() => {
113
+ const handleResize = () => {
114
+ const w = window.innerWidth;
115
+ setWidth(w);
116
+
117
+ if (w >= breakpoints["2xl"]) setBreakpoint("2xl");
118
+ else if (w >= breakpoints.xl) setBreakpoint("xl");
119
+ else if (w >= breakpoints.lg) setBreakpoint("lg");
120
+ else if (w >= breakpoints.md) setBreakpoint("md");
121
+ else if (w >= breakpoints.sm) setBreakpoint("sm");
122
+ else setBreakpoint(null);
123
+ };
124
+
125
+ handleResize();
126
+ window.addEventListener("resize", handleResize);
127
+ return () => window.removeEventListener("resize", handleResize);
128
+ }, []);
129
+
130
+ const isAbove = (bp: Breakpoint) => width >= breakpoints[bp];
131
+ const isBelow = (bp: Breakpoint) => width < breakpoints[bp];
132
+
133
+ return { breakpoint, width, isAbove, isBelow };
134
+ }
135
+
136
+ // hooks/useMediaQuery.ts
137
+ import { useState, useEffect } from "react";
138
+
139
+ export function useMediaQuery(query: string): boolean {
140
+ const [matches, setMatches] = useState(false);
141
+
142
+ useEffect(() => {
143
+ const media = window.matchMedia(query);
144
+ if (media.matches !== matches) {
145
+ setMatches(media.matches);
146
+ }
147
+
148
+ const listener = (e: MediaQueryListEvent) => setMatches(e.matches);
149
+ media.addEventListener("change", listener);
150
+ return () => media.removeEventListener("change", listener);
151
+ }, [matches, query]);
152
+
153
+ return matches;
154
+ }
155
+
156
+ // Usage
157
+ function Component() {
158
+ const isMobile = useMediaQuery("(max-width: 639px)");
159
+ const isTablet = useMediaQuery("(min-width: 640px) and (max-width: 1023px)");
160
+ const isDesktop = useMediaQuery("(min-width: 1024px)");
161
+ const prefersDark = useMediaQuery("(prefers-color-scheme: dark)");
162
+ const prefersReducedMotion = useMediaQuery("(prefers-reduced-motion: reduce)");
163
+
164
+ return (
165
+ <div>
166
+ {isMobile && <MobileLayout />}
167
+ {isTablet && <TabletLayout />}
168
+ {isDesktop && <DesktopLayout />}
169
+ </div>
170
+ );
171
+ }
172
+ ```
173
+
174
+ ### 2. Fluid Typography
20
175
 
21
- ### Fluid Typography
22
176
  ```css
23
- font-size: clamp(1rem, 2.5vw, 2rem);
177
+ /* Fluid typography using clamp() */
178
+ :root {
179
+ /* Base font sizes */
180
+ --font-size-xs: clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem);
181
+ --font-size-sm: clamp(0.875rem, 0.8rem + 0.35vw, 1rem);
182
+ --font-size-base: clamp(1rem, 0.9rem + 0.5vw, 1.125rem);
183
+ --font-size-lg: clamp(1.125rem, 1rem + 0.6vw, 1.25rem);
184
+ --font-size-xl: clamp(1.25rem, 1.1rem + 0.75vw, 1.5rem);
185
+ --font-size-2xl: clamp(1.5rem, 1.25rem + 1.25vw, 2rem);
186
+ --font-size-3xl: clamp(1.875rem, 1.5rem + 1.875vw, 2.5rem);
187
+ --font-size-4xl: clamp(2.25rem, 1.75rem + 2.5vw, 3rem);
188
+ --font-size-5xl: clamp(3rem, 2rem + 5vw, 4rem);
189
+
190
+ /* Fluid spacing */
191
+ --space-xs: clamp(0.25rem, 0.2rem + 0.25vw, 0.5rem);
192
+ --space-sm: clamp(0.5rem, 0.4rem + 0.5vw, 0.75rem);
193
+ --space-md: clamp(1rem, 0.8rem + 1vw, 1.5rem);
194
+ --space-lg: clamp(1.5rem, 1.2rem + 1.5vw, 2.5rem);
195
+ --space-xl: clamp(2rem, 1.5rem + 2.5vw, 4rem);
196
+ --space-2xl: clamp(3rem, 2rem + 5vw, 6rem);
197
+ }
198
+
199
+ /* Typography scale */
200
+ h1 {
201
+ font-size: var(--font-size-4xl);
202
+ line-height: 1.1;
203
+ letter-spacing: -0.02em;
204
+ }
205
+
206
+ h2 {
207
+ font-size: var(--font-size-3xl);
208
+ line-height: 1.2;
209
+ letter-spacing: -0.01em;
210
+ }
211
+
212
+ h3 {
213
+ font-size: var(--font-size-2xl);
214
+ line-height: 1.3;
215
+ }
216
+
217
+ h4 {
218
+ font-size: var(--font-size-xl);
219
+ line-height: 1.4;
220
+ }
221
+
222
+ p {
223
+ font-size: var(--font-size-base);
224
+ line-height: 1.6;
225
+ }
226
+
227
+ /* Responsive text utilities */
228
+ .text-balance {
229
+ text-wrap: balance;
230
+ }
231
+
232
+ .text-pretty {
233
+ text-wrap: pretty;
234
+ }
235
+ ```
236
+
237
+ ### 3. Flexible Grid Layouts
238
+
239
+ ```css
240
+ /* Auto-fit responsive grid */
241
+ .auto-grid {
242
+ display: grid;
243
+ grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
244
+ gap: var(--space-md);
245
+ }
246
+
247
+ /* Auto-fill variant */
248
+ .auto-fill-grid {
249
+ display: grid;
250
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
251
+ gap: var(--space-md);
252
+ }
253
+
254
+ /* Sidebar layout */
255
+ .sidebar-layout {
256
+ display: grid;
257
+ grid-template-columns: 1fr;
258
+ gap: var(--space-lg);
259
+ }
260
+
261
+ @media (min-width: 768px) {
262
+ .sidebar-layout {
263
+ grid-template-columns: minmax(200px, 25%) 1fr;
264
+ }
265
+ }
266
+
267
+ /* Holy grail layout */
268
+ .holy-grail {
269
+ display: grid;
270
+ grid-template-rows: auto 1fr auto;
271
+ min-height: 100vh;
272
+ }
273
+
274
+ .holy-grail-main {
275
+ display: grid;
276
+ grid-template-columns: 1fr;
277
+ gap: var(--space-md);
278
+ }
279
+
280
+ @media (min-width: 768px) {
281
+ .holy-grail-main {
282
+ grid-template-columns: minmax(150px, 20%) 1fr;
283
+ }
284
+ }
285
+
286
+ @media (min-width: 1024px) {
287
+ .holy-grail-main {
288
+ grid-template-columns: minmax(200px, 20%) 1fr minmax(200px, 20%);
289
+ }
290
+ }
291
+
292
+ /* Masonry-like layout with CSS Grid */
293
+ .masonry-grid {
294
+ display: grid;
295
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
296
+ grid-auto-rows: 10px;
297
+ gap: var(--space-md);
298
+ }
299
+
300
+ /* Card spans based on content */
301
+ .masonry-item--small {
302
+ grid-row: span 20;
303
+ }
304
+
305
+ .masonry-item--medium {
306
+ grid-row: span 30;
307
+ }
308
+
309
+ .masonry-item--large {
310
+ grid-row: span 40;
311
+ }
312
+ ```
313
+
314
+ ```tsx
315
+ // Responsive grid component
316
+ interface ResponsiveGridProps {
317
+ children: React.ReactNode;
318
+ minWidth?: string;
319
+ gap?: string;
320
+ className?: string;
321
+ }
322
+
323
+ export function ResponsiveGrid({
324
+ children,
325
+ minWidth = "300px",
326
+ gap = "1rem",
327
+ className = "",
328
+ }: ResponsiveGridProps) {
329
+ return (
330
+ <div
331
+ className={className}
332
+ style={{
333
+ display: "grid",
334
+ gridTemplateColumns: `repeat(auto-fit, minmax(min(${minWidth}, 100%), 1fr))`,
335
+ gap,
336
+ }}
337
+ >
338
+ {children}
339
+ </div>
340
+ );
341
+ }
342
+
343
+ // Usage
344
+ function ProductGrid() {
345
+ return (
346
+ <ResponsiveGrid minWidth="280px" gap="1.5rem">
347
+ {products.map((product) => (
348
+ <ProductCard key={product.id} product={product} />
349
+ ))}
350
+ </ResponsiveGrid>
351
+ );
352
+ }
24
353
  ```
25
354
 
26
- ### Responsive Grid
355
+ ### 4. Container Queries
356
+
27
357
  ```css
28
- .grid {
358
+ /* Container query setup */
359
+ .card-container {
360
+ container-type: inline-size;
361
+ container-name: card;
362
+ }
363
+
364
+ /* Base card styles (smallest size) */
365
+ .card {
366
+ display: flex;
367
+ flex-direction: column;
368
+ padding: 1rem;
369
+ }
370
+
371
+ .card-image {
372
+ width: 100%;
373
+ aspect-ratio: 16/9;
374
+ object-fit: cover;
375
+ }
376
+
377
+ .card-content {
378
+ padding: 1rem 0;
379
+ }
380
+
381
+ /* Card layout changes based on container width */
382
+ @container card (min-width: 400px) {
383
+ .card {
384
+ flex-direction: row;
385
+ gap: 1rem;
386
+ }
387
+
388
+ .card-image {
389
+ width: 40%;
390
+ aspect-ratio: 1;
391
+ }
392
+
393
+ .card-content {
394
+ flex: 1;
395
+ display: flex;
396
+ flex-direction: column;
397
+ justify-content: center;
398
+ }
399
+ }
400
+
401
+ @container card (min-width: 600px) {
402
+ .card {
403
+ padding: 1.5rem;
404
+ }
405
+
406
+ .card-image {
407
+ width: 35%;
408
+ }
409
+
410
+ .card-title {
411
+ font-size: 1.5rem;
412
+ }
413
+ }
414
+
415
+ /* Container query units */
416
+ .responsive-text {
417
+ font-size: clamp(1rem, 3cqi, 1.5rem);
418
+ padding: 2cqi;
419
+ }
420
+
421
+ /* Style queries (experimental) */
422
+ @container style(--theme: dark) {
423
+ .card {
424
+ background: #1a1a1a;
425
+ color: white;
426
+ }
427
+ }
428
+ ```
429
+
430
+ ```tsx
431
+ // React component with container queries
432
+ export function ContainerQueryCard({ title, description, image }: CardProps) {
433
+ return (
434
+ <div className="card-container">
435
+ <article className="card">
436
+ <img src={image} alt="" className="card-image" />
437
+ <div className="card-content">
438
+ <h3 className="card-title">{title}</h3>
439
+ <p className="card-description">{description}</p>
440
+ </div>
441
+ </article>
442
+ </div>
443
+ );
444
+ }
445
+
446
+ // CSS Module with container queries
447
+ // Card.module.css
448
+ /*
449
+ .container {
450
+ container-type: inline-size;
451
+ }
452
+
453
+ .card {
29
454
  display: grid;
30
- grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
31
455
  gap: 1rem;
32
456
  }
457
+
458
+ @container (min-width: 30rem) {
459
+ .card {
460
+ grid-template-columns: 1fr 2fr;
461
+ }
462
+ }
463
+ */
33
464
  ```
34
465
 
35
- ### Container Queries
466
+ ### 5. Responsive Images
467
+
468
+ ```tsx
469
+ // Responsive image component
470
+ interface ResponsiveImageProps {
471
+ src: string;
472
+ alt: string;
473
+ sizes?: string;
474
+ className?: string;
475
+ priority?: boolean;
476
+ }
477
+
478
+ export function ResponsiveImage({
479
+ src,
480
+ alt,
481
+ sizes = "100vw",
482
+ className,
483
+ priority = false,
484
+ }: ResponsiveImageProps) {
485
+ // Generate srcset for different sizes
486
+ const widths = [320, 640, 768, 1024, 1280, 1536, 1920];
487
+ const srcSet = widths
488
+ .map((w) => `${src}?w=${w} ${w}w`)
489
+ .join(", ");
490
+
491
+ return (
492
+ <img
493
+ src={src}
494
+ srcSet={srcSet}
495
+ sizes={sizes}
496
+ alt={alt}
497
+ className={className}
498
+ loading={priority ? "eager" : "lazy"}
499
+ decoding={priority ? "sync" : "async"}
500
+ />
501
+ );
502
+ }
503
+
504
+ // Picture element for art direction
505
+ export function ArtDirectedImage({
506
+ mobileSrc,
507
+ tabletSrc,
508
+ desktopSrc,
509
+ alt,
510
+ }: {
511
+ mobileSrc: string;
512
+ tabletSrc: string;
513
+ desktopSrc: string;
514
+ alt: string;
515
+ }) {
516
+ return (
517
+ <picture>
518
+ {/* Desktop - landscape image */}
519
+ <source media="(min-width: 1024px)" srcSet={desktopSrc} />
520
+ {/* Tablet - square image */}
521
+ <source media="(min-width: 640px)" srcSet={tabletSrc} />
522
+ {/* Mobile - portrait image (default) */}
523
+ <img src={mobileSrc} alt={alt} loading="lazy" />
524
+ </picture>
525
+ );
526
+ }
527
+
528
+ // Background image with responsive behavior
529
+ export function ResponsiveHero({ children }: { children: React.ReactNode }) {
530
+ return (
531
+ <section
532
+ className="
533
+ relative min-h-[50vh] md:min-h-[60vh] lg:min-h-[70vh]
534
+ bg-cover bg-center bg-no-repeat
535
+ bg-[image:var(--mobile-bg)]
536
+ md:bg-[image:var(--tablet-bg)]
537
+ lg:bg-[image:var(--desktop-bg)]
538
+ "
539
+ style={{
540
+ "--mobile-bg": "url('/hero-mobile.jpg')",
541
+ "--tablet-bg": "url('/hero-tablet.jpg')",
542
+ "--desktop-bg": "url('/hero-desktop.jpg')",
543
+ } as React.CSSProperties}
544
+ >
545
+ <div className="absolute inset-0 bg-black/50" />
546
+ <div className="relative z-10 flex items-center justify-center h-full">
547
+ {children}
548
+ </div>
549
+ </section>
550
+ );
551
+ }
552
+ ```
553
+
554
+ ### 6. Touch-Friendly Design
555
+
36
556
  ```css
37
- @container (min-width: 400px) {
38
- .card { flex-direction: row; }
557
+ /* Touch target sizes (minimum 44x44px) */
558
+ .touch-target {
559
+ min-width: 44px;
560
+ min-height: 44px;
561
+ display: flex;
562
+ align-items: center;
563
+ justify-content: center;
564
+ }
565
+
566
+ /* Larger touch targets on mobile */
567
+ @media (pointer: coarse) {
568
+ .button,
569
+ .link,
570
+ .interactive {
571
+ min-height: 48px;
572
+ padding: 12px 16px;
573
+ }
574
+
575
+ /* Increase spacing between interactive elements */
576
+ .button-group {
577
+ gap: 12px;
578
+ }
579
+
580
+ /* Larger form inputs */
581
+ input,
582
+ select,
583
+ textarea {
584
+ min-height: 48px;
585
+ font-size: 16px; /* Prevents iOS zoom on focus */
586
+ }
587
+ }
588
+
589
+ /* Hover only on devices that support it */
590
+ @media (hover: hover) {
591
+ .card:hover {
592
+ transform: translateY(-4px);
593
+ box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);
594
+ }
595
+
596
+ .button:hover {
597
+ background-color: var(--color-primary-dark);
598
+ }
599
+ }
600
+
601
+ /* Active states for touch devices */
602
+ @media (hover: none) {
603
+ .card:active {
604
+ transform: scale(0.98);
605
+ }
606
+
607
+ .button:active {
608
+ background-color: var(--color-primary-dark);
609
+ }
610
+ }
611
+
612
+ /* Disable hover effects on touch */
613
+ @media (hover: none) and (pointer: coarse) {
614
+ .hover-effect {
615
+ transform: none !important;
616
+ }
617
+ }
618
+
619
+ /* Safe area insets for notched devices */
620
+ .safe-area-padding {
621
+ padding-left: env(safe-area-inset-left);
622
+ padding-right: env(safe-area-inset-right);
623
+ padding-bottom: env(safe-area-inset-bottom);
624
+ }
625
+
626
+ .fixed-bottom-nav {
627
+ position: fixed;
628
+ bottom: 0;
629
+ left: 0;
630
+ right: 0;
631
+ padding-bottom: calc(1rem + env(safe-area-inset-bottom));
632
+ }
633
+ ```
634
+
635
+ ```tsx
636
+ // Touch-friendly button component
637
+ export function TouchButton({
638
+ children,
639
+ onClick,
640
+ ...props
641
+ }: React.ButtonHTMLAttributes<HTMLButtonElement>) {
642
+ return (
643
+ <button
644
+ onClick={onClick}
645
+ className="
646
+ min-h-[44px] min-w-[44px]
647
+ px-4 py-2
648
+ touch-manipulation
649
+ select-none
650
+ active:scale-95
651
+ transition-transform duration-150
652
+ "
653
+ {...props}
654
+ >
655
+ {children}
656
+ </button>
657
+ );
658
+ }
659
+
660
+ // Swipe gesture hook
661
+ import { useState, useRef } from "react";
662
+
663
+ interface SwipeHandlers {
664
+ onSwipeLeft?: () => void;
665
+ onSwipeRight?: () => void;
666
+ onSwipeUp?: () => void;
667
+ onSwipeDown?: () => void;
668
+ }
669
+
670
+ export function useSwipe(handlers: SwipeHandlers, threshold = 50) {
671
+ const touchStart = useRef<{ x: number; y: number } | null>(null);
672
+
673
+ const onTouchStart = (e: React.TouchEvent) => {
674
+ touchStart.current = {
675
+ x: e.touches[0].clientX,
676
+ y: e.touches[0].clientY,
677
+ };
678
+ };
679
+
680
+ const onTouchEnd = (e: React.TouchEvent) => {
681
+ if (!touchStart.current) return;
682
+
683
+ const deltaX = e.changedTouches[0].clientX - touchStart.current.x;
684
+ const deltaY = e.changedTouches[0].clientY - touchStart.current.y;
685
+
686
+ if (Math.abs(deltaX) > Math.abs(deltaY)) {
687
+ if (deltaX > threshold) handlers.onSwipeRight?.();
688
+ if (deltaX < -threshold) handlers.onSwipeLeft?.();
689
+ } else {
690
+ if (deltaY > threshold) handlers.onSwipeDown?.();
691
+ if (deltaY < -threshold) handlers.onSwipeUp?.();
692
+ }
693
+
694
+ touchStart.current = null;
695
+ };
696
+
697
+ return { onTouchStart, onTouchEnd };
698
+ }
699
+ ```
700
+
701
+ ### 7. Responsive Navigation
702
+
703
+ ```tsx
704
+ // Responsive navigation component
705
+ import { useState } from "react";
706
+
707
+ export function ResponsiveNav() {
708
+ const [isOpen, setIsOpen] = useState(false);
709
+
710
+ return (
711
+ <nav className="relative bg-white shadow-sm">
712
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
713
+ <div className="flex justify-between h-16">
714
+ {/* Logo */}
715
+ <div className="flex-shrink-0 flex items-center">
716
+ <Logo />
717
+ </div>
718
+
719
+ {/* Desktop navigation */}
720
+ <div className="hidden md:flex md:items-center md:space-x-8">
721
+ <NavLink href="/">Home</NavLink>
722
+ <NavLink href="/products">Products</NavLink>
723
+ <NavLink href="/about">About</NavLink>
724
+ <NavLink href="/contact">Contact</NavLink>
725
+ </div>
726
+
727
+ {/* Mobile menu button */}
728
+ <div className="flex items-center md:hidden">
729
+ <button
730
+ onClick={() => setIsOpen(!isOpen)}
731
+ className="
732
+ inline-flex items-center justify-center
733
+ p-2 rounded-md
734
+ text-gray-600 hover:text-gray-900 hover:bg-gray-100
735
+ focus:outline-none focus:ring-2 focus:ring-inset focus:ring-blue-500
736
+ "
737
+ aria-expanded={isOpen}
738
+ aria-controls="mobile-menu"
739
+ >
740
+ <span className="sr-only">
741
+ {isOpen ? "Close menu" : "Open menu"}
742
+ </span>
743
+ {isOpen ? (
744
+ <XIcon className="h-6 w-6" />
745
+ ) : (
746
+ <MenuIcon className="h-6 w-6" />
747
+ )}
748
+ </button>
749
+ </div>
750
+ </div>
751
+ </div>
752
+
753
+ {/* Mobile menu */}
754
+ <div
755
+ id="mobile-menu"
756
+ className={`
757
+ md:hidden
758
+ ${isOpen ? "block" : "hidden"}
759
+ `}
760
+ >
761
+ <div className="px-2 pt-2 pb-3 space-y-1 bg-white border-t">
762
+ <MobileNavLink href="/" onClick={() => setIsOpen(false)}>
763
+ Home
764
+ </MobileNavLink>
765
+ <MobileNavLink href="/products" onClick={() => setIsOpen(false)}>
766
+ Products
767
+ </MobileNavLink>
768
+ <MobileNavLink href="/about" onClick={() => setIsOpen(false)}>
769
+ About
770
+ </MobileNavLink>
771
+ <MobileNavLink href="/contact" onClick={() => setIsOpen(false)}>
772
+ Contact
773
+ </MobileNavLink>
774
+ </div>
775
+ </div>
776
+ </nav>
777
+ );
778
+ }
779
+
780
+ function MobileNavLink({
781
+ href,
782
+ children,
783
+ onClick,
784
+ }: {
785
+ href: string;
786
+ children: React.ReactNode;
787
+ onClick: () => void;
788
+ }) {
789
+ return (
790
+ <a
791
+ href={href}
792
+ onClick={onClick}
793
+ className="
794
+ block px-3 py-2 rounded-md
795
+ text-base font-medium
796
+ text-gray-700 hover:text-gray-900 hover:bg-gray-50
797
+ min-h-[44px] flex items-center
798
+ "
799
+ >
800
+ {children}
801
+ </a>
802
+ );
803
+ }
804
+ ```
805
+
806
+ ## Use Cases
807
+
808
+ ### Responsive Card Layout
809
+
810
+ ```tsx
811
+ export function ResponsiveCardGrid() {
812
+ return (
813
+ <div
814
+ className="
815
+ grid gap-4 sm:gap-6
816
+ grid-cols-1
817
+ sm:grid-cols-2
818
+ lg:grid-cols-3
819
+ xl:grid-cols-4
820
+ "
821
+ >
822
+ {cards.map((card) => (
823
+ <Card key={card.id} className="h-full">
824
+ <CardImage src={card.image} alt={card.title} />
825
+ <CardContent>
826
+ <CardTitle className="line-clamp-2">{card.title}</CardTitle>
827
+ <CardDescription className="line-clamp-3">
828
+ {card.description}
829
+ </CardDescription>
830
+ </CardContent>
831
+ </Card>
832
+ ))}
833
+ </div>
834
+ );
39
835
  }
40
836
  ```
41
837
 
42
838
  ## Best Practices
43
- - Mobile-first approach
44
- - Use relative units
839
+
840
+ ### Do's
841
+
842
+ - Start with mobile-first CSS
843
+ - Use relative units (rem, em, %)
45
844
  - Test on real devices
46
- - Consider touch targets (44px min)
845
+ - Use semantic HTML
846
+ - Implement touch-friendly targets
847
+ - Consider reduced motion preferences
848
+ - Use container queries for components
849
+ - Optimize images for different sizes
850
+ - Test across browsers
851
+ - Use CSS logical properties
852
+
853
+ ### Don'ts
854
+
855
+ - Don't hide content on mobile unnecessarily
856
+ - Don't use fixed widths
857
+ - Don't rely only on hover states
858
+ - Don't use small touch targets
859
+ - Don't ignore landscape orientation
860
+ - Don't skip accessibility testing
861
+ - Don't use device-specific breakpoints
862
+ - Don't forget keyboard navigation
863
+ - Don't ignore safe area insets
864
+ - Don't assume mouse input
865
+
866
+ ## References
867
+
868
+ - [CSS Media Queries](https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries)
869
+ - [Container Queries](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Container_Queries)
870
+ - [Responsive Images](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images)
871
+ - [Touch Events](https://developer.mozilla.org/en-US/docs/Web/API/Touch_events)
872
+ - [Viewport Concepts](https://developer.mozilla.org/en-US/docs/Web/CSS/Viewport_concepts)