Skip to content
Native Animation
Security Status Gauge animation preview

This preview uses fictional demo content. See the props below for the customization options available in this component.

npx nativeanimation add security-status-gauge

Security Status Gauge

A security risk gauge with rolling totals and expandable history, with configurable devices, sessions, palette, and fonts.

$9 for this animation

Add to your project

Copy the component and its assets into your app with the Native Animation CLI, or paste the agent prompt into Claude Code, Codex or Cursor once the MCP server is connected.

About this animation

A security risk gauge with rolling totals and expandable history, with configurable devices, sessions, palette, and fonts.

Why this one

Security dashboards usually leave the risk number static. This piece connects a concrete action to its consequence: revoking access folds the session away, rolls the session counter, lowers the meter, and fades into a calmer risk label. The Nimbus demo changes from 55% and 2/8 to 38% and 1/7.

How it works

  1. The host supplies device tiles and session metadata, including dates and city codes formatted for its locale.
  2. A single shared risk value controls the Skia arc length, accent color, rolling percentage, and threshold label fades. The intro fills it from zero in 580 ms.
  3. A horizontal pan follows the finger; its velocity projects to the open or closed action position. A tap reveals the same action. Pressure uses a separate shared value.
  4. Revoking a session updates discrete data once. Critical springs fold the row and settle the risk and counters without overshoot. Filtering never changes the risk calculation.
  5. Digit columns roll only on their own carry, fade by distance, and rest precisely on whole digits; the slash and percent have separate widths.
  6. The demo calls the imperative API at 1.5 s (laptop filter), 2.5 s (clear), 3.3 s (reveal), 3.9 s (revoke), and 6.1 s (restore). Direct interaction cancels all pending demo timers.

Install

Requires an Expo SDK 58 project with a Gesture Handler root. Copy this folder without demo/.

npx expo install @shopify/react-native-skia@2.14.0 expo-blur@~58.0.5 expo-image@~58.0.17 expo-linear-gradient@~58.0.3 react-native-gesture-handler@~3.3.0 react-native-reanimated@4.7.1 react-native-worklets@0.13.0

Configure Reanimated/worklets as required by your host. Skia on web requires the host's CanvasKit setup; this component does not initialize the app renderer. Icons use the included Lucide paths as local SVG data, avoiding one WebGL context per icon. The back button uses native blur on iOS and a translucent surface fallback elsewhere.

Usage

import { SecurityStatusGauge } from './security-status-gauge';

<SecurityStatusGauge
  devices={[
    { id: 'work', name: 'Work laptop', icon: 'laptop', detail: '2 items' },
  ]}
  sessions={[
    { id: 'session-1', deviceId: 'work', name: 'Alex Morgan', initials: 'AM', date: 'Oct 12, 1:00 PM', city: 'PAR' },
  ]}
  riskForSessions={(remaining) => remaining.length ? 55 : 12}
  onRevoke={(id) => revokeAccessOnYourServer(id)}
/>
const gauge = useRef<SecurityStatusGaugeRef>(null);

<SecurityStatusGauge
  ref={gauge}
  devices={devices}
  sessions={sessions}
  totalSessions={8}
  riskForSessions={(remaining) => remaining.length === 2 ? 55 : 38}
  thresholds={[40, 75]}
  strokeRatio={0.12}
  palette={{ low: '#B3EA68', medium: '#F2F23A' }}
  copy={{ security: 'Access health', revoke: 'End session' }}
  fontFamily="YourInterfaceFont"
  numberFontFamily="YourLightFont"
  onDirectoryPress={() => openDirectory()}
  onTeamPress={() => openTeam()}
/>

// From event handlers:
gauge.current?.filter('work');
gauge.current?.filter(null);
gauge.current?.reveal('session-1');
gauge.current?.revoke('session-1');
gauge.current?.reset();

Revocations are local and optimistic: the host owns persistence, authorization, and errors. onRevoke is called once per local session ID until reset(). If you supply risk instead of riskForSessions, update that prop yourself after an action. reset() restores the currently supplied data; remount with a new key when replacing an entire session dataset. Device IDs must match those in sessions. The demo's risk formula is illustrative, not a security scoring algorithm.

Props

PropTypeDefaultPurpose
devicesSecurityDevice[]requiredIDs, names, kit icon names, caller-formatted count labels
sessionsSecuritySession[]requiredIDs, device IDs, names, initials, caller-formatted dates and city codes
riskForSessions(remaining) => number—Risk calculator, receives all remaining sessions
risknumber0Host-controlled 0–100 risk, used without a calculator
totalSessionsnumbersessions.lengthCounter denominator, decremented by local revocations
onRevoke(id) => void—Persist a local revocation
onFilterChange(deviceId | null) => void—Observe filtering
onInteraction() => void—Stop host autoplay on touch, scroll, or accessibility action
onBackPress() => voidresetBack control action
onDirectoryPress() => voidclear filterDirectory control action
onTeamPress() => voidfirst device filterTeam control action
palettePartial<SecurityPalette>slate/light/dark card paletteAll background, ink, border, glass, avatar, shadow, and risk colors
copyPartial<SecurityCopy>English structural labelsAll titles, actions, risk labels, empty state, hint, percent symbol
thresholdsreadonly [number, number][40, 75]Ordered low/medium and medium/high boundaries
fontFamilystringsystemBody and control font
numberFontFamilystringbody fontFigures; demo uses Geist Light
brand{ name, colors, glyph, ink }noneHost brand tile and wordmark
strokeRationumber0.12Arc thickness relative to gauge width, bounded to 0.06–0.18
entranceDurationnumber580Intro duration; action springs remain critical
formatAnnouncement(event, value, risk) => stringaction plus riskCaller-localized announcements
accessibilityLabelstringsecurity titleGauge accessible name
styleStyleProp<ViewStyle>—Root styling and host safe-area padding
testIDstringsecurity-status-gaugeStable root/control identifiers

ref: revoke(id), filter(deviceId | null), reveal(id), reset().

Layout and themes

The root fills its host with flex: 1, measures width with onLayout, and caps content width at 468 pt on tablets. Content stays in vertical flow with scrolling in short containers or landscape; device tiles scroll horizontally. The gauge becomes smaller below 350 pt. This is an embeddable product panel: the host supplies safe-area insets. The demo reserves 64 pt above navigation and places the brand beside the title, below the viewer close control. Full palette overrides allow either light or dark host themes without hard-coded body text colors. There are no remote assets, photos, or flags.

Compatibility

Expo SDK 58; React Native 0.88.0; Reanimated 4.7.1; worklets 0.13.0; Skia 2.14.0. Uses installed Expo Go modules.

Accessibility

Controls are at least 48 pt; session rows are at least 64 pt and grow with content. Buttons expose roles, labels, selection, and stable test IDs. Session gestures have a tap alternative and an accessibility action for revocation. Filtering and revoking announce the action and new risk. Hidden or revoked rows are removed from accessibility and touch routing. Reduced motion uses 200 ms fades, stationary digits, and no entrance translation; actions appear beside the row without a sliding front surface.

Icons: Lucide (ISC). Demo brand: Nimbus from the repository identity kit. No reference brand assets are included.