This page catalogues every interaction pattern in the DomiDo Progressive Web App: how the user touches a screen, how the system feeds back, how transitions move, how forms validate, how loading shows up, and how every recoverable action can be undone. Every pattern is built on React Native, React Native Web, and React Three Fiber, and every pattern below is referenced from at least one customer or admin surface in the product. The patterns are deliberately consistent: a swipe always behaves the same way; an undo toast always lasts the same length; a 3D viewer always reacts to the same gestures; a form always validates at the same moments. Newcomers reading this page alongside a screen wireframe should be able to predict what every gesture, button, and field will do without testing.
Gallery browse, order history, the notifications list, and My Designs support pull-to-refresh. A pull of eighty pixels triggers a thirty-two-pixel sage-coloured circular spinner centred sixteen pixels below the navigation bar. Past the threshold the pull moves at half finger speed; the spinner snaps back in three hundred milliseconds. If a refresh is still running after eight seconds a "Still loading..." message appears; at fifteen seconds the refresh is abandoned with a "Couldn't refresh, pull to try again" toast. Pull-to-refresh is disabled while a filter or sort bottom sheet is open to avoid gesture conflicts.
Gallery browse, search results, comments, the admin order list, and design collections page in twenty items per request. The next page prefetches five items from the bottom. Skeleton rows render at the bottom of the list during fetch; the initial load uses a full-screen skeleton grid. An empty state offers a friendly illustration and a creation call-to-action. End-of-list shows a quiet "You've seen it all" message. Errors offer a Retry that backs off exponentially (one second, two, four) before reverting to manual retry. Scroll position survives back-navigation through an in-memory store; data older than sixty seconds invalidates. The pagination contract uses an opaque cursor, a sort field, an order, optional filters, and a hasMore flag. The response carries the items, the next cursor (null when finished), and a total count.
Swipe left on a collection item or a cart item to reveal Delete. Swipe left on a notification to dismiss, swipe right to mark as read. Eighty pixels is the trigger threshold; a swipe past sixty per cent of the row width auto-completes the action; a fast swipe (five hundred pixels per second) also auto-completes. The destructive action area is berry red, dismiss is the tertiary neutral, mark-as-read is sage. A light haptic fires at the trigger threshold. Full-swipe delete shows an Undo toast for five seconds before committing.
A long press of five hundred milliseconds opens a context. On a gallery card it opens a quick preview: a medium haptic, a scale to one-oh-two, an enlarged image with the design name, creator, and price on a frosted overlay; dragging up reveals "Add to Collection" and "Share". On a product image it opens a save-to-collection bottom sheet. On a design in the pipeline it opens a context menu (Edit, Duplicate, Share, Delete). On text it triggers native text selection. On a 3D viewer it resets the camera to the default position. Moving more than ten pixels cancels the long press.
The 3D viewer uses React Three Fiber's canvas with custom gesture handling integrated with React Native's gesture system. Single-finger drag orbits, two-finger drag pans, pinch zooms, double-tap resets to the default view, long-press also resets, and single-tap selects a block. Orbit sensitivity is zero-point-three degrees per pixel, with inertia decaying over six hundred milliseconds. Polar angle is clamped between ten and one hundred and seventy degrees so the camera cannot flip. Zoom is one-to-one with finger distance and clamped to between one-and-a-half and five times the model bounding sphere. The double-tap zoom target sits at two-and-a-half times the bounding sphere. Pan inertia matches orbit inertia. Part selection casts a ray from the tap point; a hit applies a two-pixel sage outline with a soft emissive glow and shows a tooltip with the part name. Tapping the same part deselects it; tapping empty space deselects all. A reset animation interpolates over four hundred milliseconds with ease-in-out to a default camera at two-and-a-half-times distance, thirty-degrees elevation, forty-five-degrees azimuth.
The viewer must coexist with page scroll. The container sets a vertical-pan-only touch action so the browser still handles vertical scroll; horizontal gestures inside the viewer rotate the model. Two-finger drag inside the viewer pans freely; outside the viewer it scrolls. Pinch inside the viewer zooms the model; outside, it zooms the browser. The Assembly Viewer's explode slider sits twenty pixels from the left edge with a forty-four-by-forty-four-point handle and applies a touch-action of none on the handle to prevent the back-swipe conflict. A first-use "Tap to interact" hint with a pulsing border appears below the viewer until the user touches it.
Product images, full-screen gallery images, and augmented-reality screenshots support pinch-to-zoom. Minimum scale is one, maximum is four. Double-tap toggles between one and two-and-a-half. Below one-point-one the image snaps to one. Pan is bounded to the image edges. At scale one a pull-down of more than one hundred pixels dismisses the full-screen viewer with a background fade. Swipe left and right navigates between images only at scale one.
A field validates on blur. After the first invalid blur it re-validates on every keystroke until valid. On submit, every field validates; the screen scrolls to the first error with a two-hundred-millisecond ease-out and the field is focused. An error summary toast tells the user how many issues need fixing. Visual error states are consistent: a two-pixel berry-red border, a four-pixel error ring at fifteen-per-cent opacity, a small error message four pixels below the field, an inline exclamation icon, and a matching error colour on the field label. Common rules apply across the application — email is required and validates as a well-formed address up to two hundred and fifty-four characters; passwords are not used (DomiDo is OAuth-only); names need two to fifty characters; phone numbers, if present, validate as international format; price inputs are numeric and positive; descriptions cap at five hundred characters with a visible counter; uploads cap at ten megabytes per file with type filtering.
Likes, follows, and "Add to Cart" update optimistically. A like fills the heart and increments the count instantly; an unlike reverts on server failure with a "Couldn't save like" toast. A follow flips the button to "Following" instantly. "Add to Cart" shows a check and increments the badge. Every optimistic action carries a local mutation identifier that correlates with the server response; requests that take longer than five seconds show a quiet "Saving..." indicator. Offline actions queue locally and synchronise on reconnect. Destructive actions (delete, cancel order) never use optimistic updates — the server confirms first.
Reversible actions show an undo toast at the bottom of the screen for three to five seconds before the server commits. Removing a cart item, unliking a design, unfollowing a designer, removing from a collection, dismissing a notification, and archiving an order (admin) all use this pattern. The toast carries dark-on-white text, a gold "Undo" link with a forty-four-point hit area, a subtle countdown progress bar, and slides up and fades down. Tapping Undo cancels the pending server call, restores the previous state with a brief animation, and fires a light success haptic. If a second undoable action happens, the first toast commits immediately and the second takes its place.
Every action button enters a Submitted state on tap: a sixteen-pixel inline spinner replaces the label, pointer events are disabled, and the state stays until the server responds. Every mutating request carries an idempotency key (a version-four universally unique identifier) so a re-sent request is safe. The pattern applies to Generate, Add to Cart, Pre-order, Publish, Submit Return, Refund, Send Purchase Order, and every admin mutation. On Cart load, the application checks for incomplete Stripe checkout sessions. A session younger than thirty minutes prompts an amber banner with "Complete Payment" and "Cancel" controls; an older session is auto-expired silently. This guards against phantom charges from redirect failures (crash, network loss).
Skeletons fill space during initial page load and infinite-scroll appends; spinners only appear inside buttons during submission, on filter and sort application, and as the centerpiece of 3D model load and pull-to-refresh. Image uploads use linear progress, not a spinner. A skeleton uses the elevated surface as its base colour and a forty-per-cent white shimmer that sweeps left to right over one-and-a-half seconds with ease-in-out. The shimmer is forty per cent of the skeleton width. Corner radii match the target component. Text skeletons are twelve pixels tall for body and sixteen for heading with an eight-pixel gap. Image skeletons match the target dimensions exactly. Skeletons fade in over two hundred milliseconds and cross-fade to real content over three hundred.
Haptics use the platform feedback APIs through Expo Haptics. A successful order placement fires a heavy success notification; a pipeline step completion fires a medium success. Errors fire a medium error notification. Tab taps, button presses, swipe thresholds, and pull-to-refresh triggers fire a light impact. Long-press triggers and toggle switches fire a medium impact. Slider snaps use the selection feedback. Three-dimensional part selection fires a light impact. On iOS the full haptic engine is used; on Android the Vibrator API maps these to ten-, twenty-, and thirty-millisecond patterns. A Settings toggle respects the system Vibration preference and stores the user's choice.
Page push slides in from the right over three hundred milliseconds with ease-out; pop slides out over two hundred and fifty milliseconds with ease-in. Modal present slides up over two hundred and fifty milliseconds and dismisses in two hundred. Tab switches are instant. A full-screen viewer opens and closes with a scale-and-fade between source and target. Cards fade in with an eight-pixel translateY on scroll. Toasts slide up and fade out. Dialogs scale from zero-point-nine to one with a fade. Like hearts spring to one-point-two and back. Badges scale to one-point-one-five and back. Skeleton shimmer is the same infinite ease. Status badge transitions cross-fade. The 3D viewer animations are slower because the camera carries physical meaning: camera reset is four hundred milliseconds of ease-in-out; step transitions in the Assembly Viewer take six hundred milliseconds of ease-in-out; the exploded view toggle takes five hundred milliseconds. When the operating system reports a reduced-motion preference, every slide and scale falls back to a simple two-hundred-millisecond fade. Inertia and orbit momentum are disabled. The skeleton shimmer becomes a static grey. Functional animations (progress bars, loading spinners) remain — they convey state, not delight.
The AI-mode prompt text auto-saves on text change with a two-second debounce. Projection configuration and design customisation auto-save on parameter change with a one-and-a-half-second debounce. Admin settings auto-save on field change with a three-second debounce. Profile editing does not auto-save — it has an explicit Save action. Each context shows a quiet status: a spinner with "Saving..." while in flight, a check with "Saved" that fades after two seconds, or an X with "Couldn't save" and a Retry link. Failed saves retry automatically three times with exponential backoff before reverting to manual retry. Offline edits queue locally with a "Saved locally" indicator and synchronise on reconnect. Conflict resolution is last-write-wins with the server timestamp as authority.
Every network operation has a maximum duration. AI image generation times out at one hundred and twenty seconds; 3D model generation at one hundred and eighty; pipeline processing at three hundred; file upload at one hundred and twenty; the Stripe checkout session at thirty minutes; the Server-Sent Events connection at six hundred seconds with transparent reconnection and a "Reconnecting..." message after five seconds. The third-party logistics submission has its own twenty-four-hour retry envelope. At fifty per cent of the timeout the user sees an elapsed-time counter; at eighty per cent a warning text appears; at one hundred per cent the operation fails with explicit recovery actions. A spinner is never shown indefinitely without time context.