diff --git a/packages/mobile/README.md b/packages/mobile/README.md
index dcd222344..d6d75b794 100644
--- a/packages/mobile/README.md
+++ b/packages/mobile/README.md
@@ -25,18 +25,22 @@ npm install @object-ui/mobile
## Quick Start
```tsx
-import { MobileProvider, useBreakpoint, useGesture } from '@object-ui/mobile';
+import { MobileProvider, useBreakpoint } from '@object-ui/mobile';
-function App() {
- return (
-
-
-
- );
+function MobileNav() {
+ return Mobile navigation ;
+}
+
+function DesktopSidebar() {
+ return ;
+}
+
+function MainContent() {
+ return Main content ;
}
function ResponsiveApp() {
- const { isMobile, isTablet, isDesktop } = useBreakpoint();
+ const { isMobile, isDesktop } = useBreakpoint();
return (
@@ -46,84 +50,219 @@ function ResponsiveApp() {
);
}
+
+export function App() {
+ return (
+
+
+
+ );
+}
```
## API
### MobileProvider
-Wraps your application with mobile context:
+Wraps your application with mobile context. Both props are optional: `pwa` takes a
+`PWAConfig`, `offline` takes a `PWAOfflineConfig`.
```tsx
-
-
-
+import { MobileProvider } from '@object-ui/mobile';
+
+function App() {
+ return Your application
;
+}
+
+export function Root() {
+ return (
+
+
+
+ );
+}
```
### useBreakpoint
-Hook for detecting the current breakpoint:
+Hook for detecting the current breakpoint. The current breakpoint name is
+`breakpoint` — one of `xs`, `sm`, `md`, `lg`, `xl`, `2xl`:
```tsx
-const { isMobile, isTablet, isDesktop, current } = useBreakpoint();
+import { useBreakpoint } from '@object-ui/mobile';
+
+export function BreakpointBadge() {
+ const { isMobile, isTablet, isDesktop, breakpoint, width } = useBreakpoint();
+
+ return (
+
+ {breakpoint} at {width}px — mobile {String(isMobile)}, tablet {String(isTablet)}, desktop{' '}
+ {String(isDesktop)}
+
+ );
+}
```
+`isAbove(bp)` and `isBelow(bp)` are also returned, for comparisons against a named
+breakpoint.
+
### useResponsive
-Hook for responsive values based on screen size:
+Hook for responsive values based on screen size. The keys are breakpoint names; a
+breakpoint with no entry falls back to the next smaller one that has one:
```tsx
-const columns = useResponsive({ mobile: 1, tablet: 2, desktop: 4 });
+import { useResponsive } from '@object-ui/mobile';
+
+export function ResponsiveGrid() {
+ const columns = useResponsive({ xs: 1, md: 2, lg: 4 });
+
+ return {columns} column(s)
;
+}
```
### useGesture / useSpecGesture
-Hooks for gesture detection on touch devices:
+`useGesture` detects one gesture from Object UI's direction-fused vocabulary
+(`tap`, `double-tap`, `long-press`, `swipe-left`, `swipe-right`, `swipe-up`,
+`swipe-down`, `pinch`, `rotate`, `pan`) per call, and returns a ref to attach:
```tsx
-const gestureRef = useGesture({
- onSwipeLeft: () => navigateNext(),
- onSwipeRight: () => navigateBack(),
- onPinch: (scale) => handleZoom(scale),
-});
+import { useGesture } from '@object-ui/mobile';
+
+function navigateNext() {}
+function navigateBack() {}
+
+export function SwipeArea() {
+ const nextRef = useGesture({
+ type: 'swipe-left',
+ onGesture: () => navigateNext(),
+ });
+ const backRef = useGesture({
+ type: 'swipe-right',
+ onGesture: () => navigateBack(),
+ });
+
+ return (
+
+
Swipe left for the next record
+
Swipe right to go back
+
+ );
+}
+```
+
+`useSpecGesture` takes the declarative `SpecGestureConfig` tuning shape instead, and
+dispatches to per-gesture callbacks:
+
+```tsx
+import { useSpecGesture } from '@object-ui/mobile';
+
+function handleZoom(scale: number) {
+ return scale;
+}
+
+export function PinchArea() {
+ const ref = useSpecGesture({
+ config: { type: 'pinch', enabled: true, pinch: { minScale: 0.5, maxScale: 3 } },
+ onPinch: (scale) => handleZoom(scale),
+ });
-return Swipeable content
;
+ return Pinchable content
;
+}
```
### usePullToRefresh
-Hook for pull-to-refresh behavior:
+Hook for pull-to-refresh behavior. Attach the returned `ref` to the scrollable
+container:
```tsx
-const { isRefreshing } = usePullToRefresh({
- onRefresh: async () => await fetchData(),
-});
+import { usePullToRefresh } from '@object-ui/mobile';
+
+async function fetchData(): Promise {}
+
+export function Feed() {
+ const { ref, isRefreshing, pullDistance } = usePullToRefresh({
+ onRefresh: async () => await fetchData(),
+ });
+
+ return (
+
+ {isRefreshing ? 'Refreshing…' : 'Pull to refresh'}
+
+ );
+}
```
### useTouchTarget
-Hook for ensuring minimum touch target sizes:
+Hook for ensuring minimum touch target sizes. It returns the `style` and `className`
+to spread onto the element; the defaults follow WCAG 2.5.5 (44×44 CSS pixels):
```tsx
-const { targetProps } = useTouchTarget({ minSize: 44 });
-return Tap me ;
+import { useTouchTarget } from '@object-ui/mobile';
+
+export function TapButton() {
+ const { style, className } = useTouchTarget({
+ config: { minWidth: 44, minHeight: 44 },
+ });
+
+ return (
+
+ Tap me
+
+ );
+}
```
### ResponsiveContainer
-Renders children based on breakpoint:
+Renders children based on breakpoint. Pick the range with `minBreakpoint` /
+`maxBreakpoint`, or name the breakpoints outright with `showOn` / `hideOn`:
```tsx
- } desktop={ } />
+import { ResponsiveContainer } from '@object-ui/mobile';
+
+function MobileView() {
+ return Compact layout
;
+}
+
+function DesktopView() {
+ return Full layout
;
+}
+
+export function BreakpointSwitch() {
+ return (
+
+
+
+
+
+
+
+
+ );
+}
```
### PWA Utilities
+`PWAConfig` requires `enabled`, `name` and `shortName`. `registerServiceWorker` takes
+the script `url` and `scope` plus lifecycle callbacks — the caching strategies live in
+the generated worker (`getServiceWorkerSource`), not in this call:
+
```tsx
import { generatePWAManifest, registerServiceWorker } from '@object-ui/mobile';
-const manifest = generatePWAManifest({ name: 'My App', themeColor: '#000' });
-registerServiceWorker({ cacheStrategy: 'network-first' });
+export const manifest = generatePWAManifest({
+ enabled: true,
+ name: 'My App',
+ shortName: 'My App',
+ themeColor: '#000',
+});
+
+void registerServiceWorker({ url: '/service-worker.js' });
```
## Links
diff --git a/scripts/check-doc-snippet-types.mjs b/scripts/check-doc-snippet-types.mjs
index 5024e6bf9..ca173879e 100644
--- a/scripts/check-doc-snippet-types.mjs
+++ b/scripts/check-doc-snippet-types.mjs
@@ -723,8 +723,6 @@ const UNGATED_DOCS = {
'7 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines; plus TS2554x2 TS2559x2 — candidate real defects, un-triaged',
'packages/layout/README.md':
'3 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines; 3 unresolved-module diagnostic(s)',
- 'packages/mobile/README.md':
- '19 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines; plus TS1108x2 TS2345x1 TS2353x1 — candidate real defects, un-triaged',
'packages/permissions/README.md':
'12 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines; plus TS2322x4 TS2345x1 TS2353x1 — candidate real defects, un-triaged',
'packages/plugin-ai/README.md':