--- title: Responsive and interaction hooks description: Synchronize breakpoints when behavior must follow the viewport and add ripple feedback to custom controls. order: 20 toc: - id: breakpoint-contract title: Breakpoint contract - id: reading-breakpoints title: Read breakpoints - id: root-classes title: Root classes - id: server-rendering title: Server rendering - id: ripple title: Ripple interaction --- ## Breakpoint contract {#breakpoint-contract} The shared breakpoint splits the viewport into three ranges: | Value | Range | | --------- | -------------------- | | `mobile` | Below 768px | | `tablet` | 768px through 1023px | | `desktop` | 1024px and above | Prefer CSS for visual changes. Read a breakpoint during rendering only when mounted content, interaction behavior, or data volume must change. ## Read breakpoints {#reading-breakpoints} ```tsx import { useBreakpoint, useIsDesktop, useIsMobile, useIsTablet, useOnBreakpoint, } from "@workspace/ui/hooks/use-breakpoint" const breakpoint = useBreakpoint() const isMobile = useIsMobile() ``` Every hook subscribes to the same pair of `matchMedia` queries through `useSyncExternalStore`. `useOnBreakpoint(listener)` runs only after the breakpoint changes; it does not report the initial value as a change. ## Root classes {#root-classes} After mounting `BreakpointReactor`, the document root has exactly one of `.ui:mobile`, `.ui:tablet`, or `.ui:desktop`: ```tsx import { BreakpointReactor } from "@workspace/ui/hooks/use-breakpoint" ;<> ``` `globals.css` registers these as the custom `mobile:`, `tablet:`, and `desktop:` variants, allowing CSS and behavioral code to share the same three-range definition. ## Server rendering {#server-rendering} The server snapshot is fixed to `desktop` so SSR remains deterministic. To write the real class before hydration, place the result of `getBreakpointInitializationScript()` in an inline script in the document head: ```tsx import { getBreakpointInitializationScript } from "@workspace/ui/hooks/use-breakpoint" ;