zudo-text

検索したい単語を入力

いつでも検索バーを開ける

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/ui-components/src/tokens.css:

: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-app/renderer/mobile-base.css. 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):

UtilityReturns 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-app/index.html (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-app/index.web.html, 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) on input / textarea prevents Safari's automatic zoom-on-focus behaviour, which is the most common reason users pinch-zoom in web forms.

  • --keyboard-inset keeps 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.