TerraUI v0.1.1

Shutter

The capture key, a large round console face raised over its edge. Its inner ring reads as a shutter on any ground, a camera preview included.

iPhone 17 Pro · iOS 27
A round console key raised over its edge, with an inner ring so it reads as a shutter on any ground.A round console key raised over its edge, with an inner ring so it reads as a shutter on any ground.
import Camera01Icon from "@hugeicons-pro/core-bulk-rounded/Camera01Icon";
import { HugeiconsIcon } from "@hugeicons/react-native";
import { Shutter } from "@terratcg/ui";

export default function ShutterDefault() {
  return (
    <Shutter
      accessibilityLabel="Take photo"
      onPress={() => {}}
      icon={({ color, size }) => <HugeiconsIcon icon={Camera01Icon} color={color} size={size} />}
    />
  );
}

Import

import { Shutter } from "@terratcg/ui";

Usage

<Shutter
  accessibilityLabel="Take photo"
  loading={capturing}
  onPress={capture}
  icon={({ color, size }) => <CameraIcon color={color} size={size} />}
/>

The Shutter starts a capture; the camera and the capture are yours. Set loading while a capture is in flight: the face keeps its size, shows a spinner in place of the icon and ignores presses until you clear it. The icon is optional: pass a node, or a render function that receives the face's colour and the icon size.

Examples

States

Loading holds the face with a spinner while a capture is in flight; disabled is flat and grey.

iPhone 17 Pro · iOS 27
Loading holds the face with a spinner while a capture is in flight; disabled is flat and grey.Loading holds the face with a spinner while a capture is in flight; disabled is flat and grey.
import Camera01Icon from "@hugeicons-pro/core-bulk-rounded/Camera01Icon";
import { HugeiconsIcon } from "@hugeicons/react-native";
import { Shutter } from "@terratcg/ui";
import { View } from "react-native";

export default function ShutterStates() {
  return (
    <View style={{ flexDirection: "row", justifyContent: "center", gap: 24 }}>
      <Shutter loading accessibilityLabel="Saving photo" />
      <Shutter
        disabled
        accessibilityLabel="Take photo"
        icon={({ color, size }) => <HugeiconsIcon icon={Camera01Icon} color={color} size={size} />}
      />
    </View>
  );
}

Size

The face is 76 points across by default; size sets another diameter and the icon stays 28 points.

iPhone 17 Pro · iOS 27
The face is 76 points across by default; size sets another diameter and the icon stays 28 points.The face is 76 points across by default; size sets another diameter and the icon stays 28 points.
import Camera01Icon from "@hugeicons-pro/core-bulk-rounded/Camera01Icon";
import { HugeiconsIcon } from "@hugeicons/react-native";
import { Shutter } from "@terratcg/ui";
import { View } from "react-native";

export default function ShutterSizes() {
  return (
    <View style={{ flexDirection: "row", justifyContent: "center", alignItems: "center", gap: 24 }}>
      <Shutter
        size={56}
        accessibilityLabel="Take photo, small"
        onPress={() => {}}
        icon={({ color, size }) => <HugeiconsIcon icon={Camera01Icon} color={color} size={size} />}
      />
      <Shutter
        accessibilityLabel="Take photo"
        onPress={() => {}}
        icon={({ color, size }) => <HugeiconsIcon icon={Camera01Icon} color={color} size={size} />}
      />
    </View>
  );
}

Props

Prop

Type

Shutter also accepts React Native's Pressable props, such as hitSlop and testID. SHUTTER_SIZE exports the default diameter, 76 points, for laying out the controls around it.

Accessibility

  • Role: button, with accessibilityState keeping disabled (while disabled or loading) and busy (while loading) in sync. Your own accessibilityState is merged on top.
  • Label: accessibilityLabel is required, since the face carries no text. Name the action: "Take photo".
  • Touch target: the 76 point face is well over the 44 pt and 48 dp minimums. Keep any smaller size at 48 or more.
  • Reduce Motion: the face no longer travels when pressed; the press tint stays.

Platform differences

  • iOS and Android: the same domed face, ring and travel, with no ripple.
  • Feedback: a press calls ThemeProvider's feedback handler with "press". A disabled or loading Shutter reports nothing. TerraUI plays no shutter sound or haptic itself.
  • Web: Shutter renders through react-native-web for these docs, but the web is not a supported target.

Rules

  • One Shutter per screen, for the capture only. Other camera controls are Icon buttons.
  • Set loading for as long as the capture runs, and clear it yourself: the Shutter never clears it.
  • Use onPress, never onClick, and style props for layout only: the face's colours come from the theme.

On this page