CSS Variables & Defaults

SDK-injected CSS resets, variables for safe areas, screen dimensions, and color scheme.

SDK Defaults#

The SDK automatically applies sensible defaults to every flow so you can focus on your design instead of boilerplate.

Viewport

The SDK ensures viewport-fit=cover is present on your viewport meta tag. This enables edge-to-edge rendering on iOS so your backgrounds extend behind the notch and home indicator. You don't need to add it yourself — if it's missing, the SDK appends it automatically.

CSS Resets

Base CSS resets are injected before your flow CSS. You do not need to include a CSS reset — it's already applied.

/* SDK base resets (auto-injected BEFORE your flow CSS) */
/* You do NOT need to include these — they are already applied */

*, *::before, *::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  -webkit-tap-highlight-color: transparent;
}

body {
  -webkit-font-smoothing: antialiased;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  overflow: hidden;
}
What it doesWhy
margin: 0; padding: 0 on *Removes all browser-default spacing for consistent rendering across devices
box-sizing: border-box on *Padding and borders are included in element dimensions
-webkit-tap-highlight-color: transparentRemoves the blue/gray flash on tap in iOS and Android WebViews
-webkit-font-smoothing: antialiasedCrisp text rendering on WebKit-based browsers
user-select: nonePrevents text selection for a native-app feel
-webkit-touch-callout: nonePrevents long-press context menus on links and images
overflow: hidden on bodyPrevents body scroll — use scroll containers for scrollable content

Since the resets are injected before your flow CSS, you can override any of them. For example, use user-select: text on a specific element to allow text selection, or overflow-y: auto on a scroll container.

Overriding Defaults#

All SDK defaults can be overridden in your flow CSS since it loads after the reset stylesheet.

flow.css
/* Allow text selection in a specific area */
.article-body {
  -webkit-user-select: text;
  user-select: text;
}

/* Enable scrolling in a content container */
.scrollable-content {
  overflow-y: auto;
  height: 100vh;
  -webkit-overflow-scrolling: touch;
}

/* Re-enable touch callout for links */
.external-links a {
  -webkit-touch-callout: default;
}

Available Variables#

/* CSS Variables injected by the bridge (AFTER your flow CSS) */
:root {
  /* Safe area insets (for notches, home indicators) */
  --safe-area-top: 47px;
  --safe-area-right: 0px;
  --safe-area-bottom: 34px;
  --safe-area-left: 0px;
  
  /* Screen dimensions */
  --screen-width: 393px;
  --screen-height: 852px;
  
  /* Color scheme */
  --color-scheme: light; /* or 'dark' */
  color-scheme: light;   /* enables light-dark() CSS function */
}
VariableDescriptionExample Value
--safe-area-topTop inset (status bar, notch, Dynamic Island)47px
--safe-area-bottomBottom inset (home indicator)34px
--safe-area-leftLeft inset (usually 0)0px
--safe-area-rightRight inset (usually 0)0px
--screen-widthFull screen width in points393px
--screen-heightFull screen height in points852px
--color-schemeDevice color schemelight / dark

Safe Area Usage#

Use safe area variables to ensure your content doesn't get hidden behind notches, Dynamic Island, or home indicators.

flow.css
/* Apply safe area padding to container */
.flow-container {
  padding-top: var(--safe-area-top);
  padding-bottom: var(--safe-area-bottom);
  padding-left: var(--safe-area-left);
  padding-right: var(--safe-area-right);
  min-height: 100vh;
}

/* Fixed header that respects notch */
.header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  padding-top: calc(var(--safe-area-top) + 16px);
  padding-left: calc(var(--safe-area-left) + 16px);
  padding-right: calc(var(--safe-area-right) + 16px);
}

/* Fixed bottom button that respects home indicator */
.bottom-button {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  padding: 16px;
  padding-bottom: calc(var(--safe-area-bottom) + 16px);
}

Use calc() to add extra padding beyond the safe area. For example, calc(var(--safe-area-bottom) + 16px).

Color Scheme / Dark Mode#

Support light and dark mode in your flows using the color scheme variables.

flow.css
/* Method 1: Use CSS color-scheme property */
body {
  color-scheme: var(--color-scheme);
}

/* Method 2: Use media query */
@media (prefers-color-scheme: dark) {
  body {
    background: #1a1a1a;
    color: #ffffff;
  }
}

/* Method 3: Use light-dark() function (modern browsers) */
body {
  background: light-dark(#ffffff, #1a1a1a);
  color: light-dark(#1a1a1a, #ffffff);
}

/* Method 4: Use CSS variable for manual control */
body {
  --bg-color: #ffffff;
  --text-color: #1a1a1a;
  background: var(--bg-color);
  color: var(--text-color);
}

body[data-theme="dark"] {
  --bg-color: #1a1a1a;
  --text-color: #ffffff;
}

The color-scheme property is set on :root, which enables the CSS light-dark() function and proper form control styling.

Responsive Layouts#

Use screen dimension variables for responsive layouts based on actual device size.

flow.css
/* Use screen dimensions for responsive layouts */
.hero-image {
  width: 100%;
  max-width: var(--screen-width);
  height: calc(var(--screen-height) * 0.4);
  object-fit: cover;
}

/* Full-screen background */
.full-screen {
  width: var(--screen-width);
  height: var(--screen-height);
  position: fixed;
  top: 0;
  left: 0;
}

Complete Example#

A complete flow HTML with proper safe area handling and dark mode support:

flow.html
<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    /* No CSS reset or viewport-fit needed — the SDK applies them automatically */
    
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      background: light-dark(#ffffff, #1a1a1a);
      color: light-dark(#1a1a1a, #ffffff);
      min-height: 100vh;
      display: flex;
      flex-direction: column;
    }
    
    .container {
      flex: 1;
      display: flex;
      flex-direction: column;
      padding: 24px;
      padding-top: calc(24px + var(--safe-area-top));
      padding-bottom: calc(24px + var(--safe-area-bottom));
      padding-left: max(var(--safe-area-left), 20px);
      padding-right: max(var(--safe-area-right), 20px);
    }
    
    .content {
      flex: 1;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
    }
    
    .bottom-actions {
      padding-bottom: 20px;
    }
    
    .button {
      width: 100%;
      padding: 16px;
      border-radius: 12px;
      border: none;
      font-size: 16px;
      font-weight: 600;
      cursor: pointer;
    }
    
    .button-primary {
      background: light-dark(#007AFF, #0A84FF);
      color: white;
    }
  </style>
</head>
<body>
  <div class="container">
    <div class="content">
      <h1>Welcome!</h1>
      <p>Your content here</p>
    </div>
    <div class="bottom-actions">
      <button class="button button-primary" id="continue">
        Continue
      </button>
    </div>
  </div>
  
  <script>
    document.getElementById('continue').addEventListener('click', () => {
      Mobana.haptic('success');
      Mobana.complete();
    });
  </script>
</body>
</html>

CSS vs JavaScript#

While you can also access these values via JavaScript (getSafeArea(), getColorScheme()), CSS variables are preferred for layout:

  • No JavaScript needed: Layout works immediately without waiting for JS to run.
  • Declarative: CSS is designed for layout; use it.
  • Performance: CSS variables are optimized by the browser.

Use JavaScript APIs when you need the values for logic (e.g., conditionals, calculations for canvas drawing).