Responsive Foundation
Overview
The mobile responsive contract establishes a consistent layout baseline for the zudotext iOS build and the web build viewed on mobile browsers. It defines a single breakpoint token, viewport hooks, global CSS rules that prevent common mobile layout pitfalls (keyboard displacement, bounce scrolling, input zoom), and platform-detection utilities. Together these pieces give UI components a stable surface to build on without per-component workarounds.
Breakpoint
The single breakpoint token lives in packages/:
:root {
--breakpoint-mobile: 640px;
}Anything at or below 640 px wide is treated as a mobile viewport. Components use this token in media queries to switch between desktop and mobile layouts:
@media (max-width: 640px) {
/* mobile layout */
}Hooks
useIsMobileViewport
Imported from @takazudo/ui-components. Returns true when the viewport width is at or below MOBILE_BREAKPOINT_PX.
import { useIsMobileViewport, MOBILE_BREAKPOINT_PX } from "@takazudo/ui-components";
function MyComponent() {
const isMobile = useIsMobileViewport();
return isMobile ? <MobileLayout /> : <DesktopLayout />;
}MOBILE_BREAKPOINT_PX is the numeric constant (640) exported alongside the hook. Use it when you need the value in JavaScript (e.g., a ResizeObserver threshold) rather than in CSS.
useKeyboardInset
Tracks the on-screen keyboard height on iOS using the Visual Viewport API and writes it to the --keyboard-inset CSS custom property on document.documentElement. This lets any element in the tree respond to the keyboard without JavaScript:
import { useKeyboardInset } from "@takazudo/ui-components";
function App() {
useKeyboardInset(); // call once near the root
return <Layout />;
}CSS usage pattern — keep interactive elements above the keyboard and above the iOS safe area:
.bottom-bar {
bottom: calc(var(--keyboard-inset, 0px) + env(safe-area-inset-bottom));
}var(--keyboard-inset, 0px) falls back to 0px when the keyboard is closed or the Visual Viewport API is unavailable (non-iOS browsers).
CSS Base
The renderer ships these global rules in tauri-. The viewport-only rules are scoped under @media (max-width: 640px) so the desktop build is unaffected by the app's --display-scale token system.
@media (max-width: 640px) {
/* Full viewport height with dvh — avoids address-bar collapse and
kills WKWebView outer bounce on overscroll. */
html,
body {
height: 100dvh;
overscroll-behavior: none;
}
/* Prevent iOS Safari from zooming when an input is focused.
iOS only triggers auto-zoom below 16px, so the floor matters
on mobile only. */
input,
textarea,
select {
font-size: max(16px, 1rem);
}
/* Keep focused inputs visible above the on-screen keyboard. */
input,
textarea,
select {
scroll-margin-bottom: 120px;
}
}
/* Safe-area token + opt-in utility. Apply .has-safe-top to toolbars
and floating elements at the top of the screen — NOT to the editor. */
:root {
--safe-top: env(safe-area-inset-top, 0px);
}
.has-safe-top {
padding-top: var(--safe-top);
}Platform Flag
Use these runtime utilities to branch behavior by host. They live in @takazudo/backend-bridge (next to the adapter / capability layer):
| Utility | Returns true when… |
|---|---|
isTauriIOS() | Running inside Tauri on an iOS device (iPhone, iPad, or iPadOS desktop-mode UA with touch points) |
isTauriMacOS() | Running inside Tauri on macOS (positively identified — Windows/Linux Tauri builds return false) |
isBrowser() | Running in a plain browser (no Tauri internals — covers the web build, Storybook, MockAdapter, RestAdapter) |
Example — apply a mobile CSS class only in the Tauri iOS build:
import { isTauriIOS } from "@takazudo/backend-bridge";
if (isTauriIOS()) {
document.documentElement.classList.add("is-tauri-ios");
}Accessibility Trade-off
The viewport meta tag in tauri- (the Tauri desktop/iOS entry) disables pinch-zoom for the Tauri app:
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
/>The web build uses tauri-, which omits user-scalable=no and therefore permits pinch-zoom in the browser.
user-scalable=no prevents Safari on iOS from allowing the user to pinch-zoom the Tauri app. This is a deliberate product decision — the app layout relies on fixed panels and a CodeMirror editor, which break under arbitrary zoom.
Why this is acceptable:
font-size: max(16px, 1rem)oninput/textareaprevents Safari's automatic zoom-on-focus behaviour, which is the most common reason users pinch-zoom in web forms.--keyboard-insetkeeps input fields above the on-screen keyboard, reducing the need to scroll or zoom to see what you are typing.
Known limitation: Users with low vision who rely on browser pinch-zoom for accessibility will lose that capability in the Tauri/iOS app. The browser web build (index.web.html) is unaffected — it allows pinch-zoom. iOS system accessibility (Display & Text Size → Larger Text, Accessibility Zoom) remains functional regardless of user-scalable=no.
This trade-off is accepted per product decision for the Tauri app. If accessibility requirements change, the mitigation path is to remove user-scalable=no and fix the layout issues that arise with zoom active.