Skip to content

Elevation & Shadows

Vetra UI features a refined adaptive shadow system designed for clear visual hierarchy, natural appearance, and cross-platform consistency.

Philosophy

  • Adaptive Configuration: Automatic adjustment between light and dark modes
  • Clear Visibility: Properly tuned alpha values for clear depth perception
  • Natural Hierarchy: Visible shadows that create clear separation
  • Cross-Platform Optimized: Consistent rendering across all platforms

Elevation Levels

Vetra UI provides seven standard elevation levels:

VetraTheme.shadows.none   // 0dp - No elevation
VetraTheme.shadows.xs     // 1dp - Barely perceptible
VetraTheme.shadows.sm     // 2dp - Light elevation
VetraTheme.shadows.md     // 4dp - Moderate elevation
VetraTheme.shadows.lg     // 8dp - Strong elevation
VetraTheme.shadows.xl     // 16dp - Maximum elevation
VetraTheme.shadows.xxl    // 24dp - Dramatic elevation

Elevation Scale

None - 0dp

No elevation for flat elements.

Box(
    modifier = Modifier
        .vetraShadow(elevation = VetraTheme.shadows.none)
        .background(VetraTheme.colors.canvasElevated)
)

Usage: - Flat surfaces - Embedded content - Elements flush with background

XS - 1dp

Barely perceptible elevation for subtle hover states.

VetraCard(
    modifier = Modifier
        .vetraShadow(elevation = VetraTheme.shadows.xs)
) {
    // Hovering element
}

Usage: - Hover states - Subtle separation - Slightly elevated content

SM - 2dp

Light elevation for cards and buttons.

VetraButton(onClick = { }) {
    Text("Button") // Uses shadows.sm by default
}

VetraCard {
    // Standard card elevation
}

Usage: - Cards (default) - Buttons (default) - Contained elements - List items with separation

MD - 4dp

Moderate elevation for raised elements.

VetraElevatedCard(
    modifier = Modifier
        .vetraShadow(elevation = VetraTheme.shadows.md)
) {
    // Raised card content
}

Usage: - Raised buttons - Slider thumbs - Selected items - Floating toolbars

LG - 8dp

Strong elevation for floating elements.

VetraMenu(
    expanded = showMenu,
    onDismissRequest = { showMenu = false },
    modifier = Modifier
        .vetraShadow(elevation = VetraTheme.shadows.lg)
) {
    // Menu items
}

Usage: - Dropdowns - Tooltips - Floating menus - Pop-ups

XL - 16dp

Maximum elevation for modal surfaces.

VetraDialog(
    onDismissRequest = { },
    modifier = Modifier
        .vetraShadow(elevation = VetraTheme.shadows.xl)
) {
    // Dialog content
}

Usage: - Dialogs (default) - Navigation drawers - Modal sheets - Prominent overlays

XXL - 24dp

Dramatic elevation for critical alerts.

Box(
    modifier = Modifier
        .fillMaxWidth()
        .vetraShadow(
            elevation = VetraTheme.shadows.xxl,
            shape = VetraTheme.shapes.xl
        )
        .background(VetraTheme.colors.danger)
) {
    // Critical alert content
}

Usage: - Critical modals - Full-screen overlays - Emergency alerts - Splash screens

Usage

Basic Usage

Apply adaptive shadows with the vetraShadow modifier:

Box(
    modifier = Modifier
        .vetraShadow(
            elevation = VetraTheme.shadows.md,
            shape = VetraTheme.shapes.md
        )
        .background(VetraTheme.colors.canvasElevated, VetraTheme.shapes.md)
) {
    // Your content
}

The shadow automatically adapts based on theme mode: - Light mode: 15% ambient, 25% spot shadows (1.15× spread) - Dark mode: 25% ambient, 35% spot shadows (1.2× spread)

Advanced Configuration

Customize shadow appearance:

Modifier.vetraShadow(
    elevation = VetraTheme.shadows.lg,
    shape = VetraTheme.shapes.md,
    clip = false  // Don't clip content to shape
)

Parameters: - elevation: Shadow height (Dp value) - shape: Shadow shape (should match element shape) - clip: Clip content to shape (default: false)

Custom Shadow Config

For special cases, provide custom shadow configuration:

VetraTheme(
    darkMode = false,
    shadowConfig = ShadowConfig(
        ambientAlpha = 0.15f,
        spotAlpha = 0.20f,
        elevationMultiplier = 1.2f
    )
) {
    // Your app content
}

Examples

Card with Shadow

@Composable
fun ElevatedContent() {
    VetraCard(
        modifier = Modifier
            .fillMaxWidth()
            .vetraShadow(
                elevation = VetraTheme.shadows.md,
                shape = VetraTheme.shapes.md
            )
    ) {
        Column(
            modifier = Modifier.padding(16.dp)
        ) {
            Text(
                text = "Elevated Card",
                style = VetraTheme.typography.headingMd
            )
            Text(
                text = "With beautiful shadow",
                style = VetraTheme.typography.bodyMd,
                color = VetraTheme.colors.textSecondary
            )
        }
    }
}

Floating Action Button

@Composable
fun FloatingActionButton() {
    VetraFilledIconButton(
        onClick = { },
        modifier = Modifier
            .size(56.dp)
            .vetraShadow(
                elevation = VetraTheme.shadows.lg,
                shape = VetraTheme.shapes.full
            )
    ) {
        Icon(
            imageVector = Icons.Default.Add,
            contentDescription = "Add"
        )
    }
}

Dialog with Shadow

@Composable
fun ConfirmDialog(
    onDismiss: () -> Unit,
    onConfirm: () -> Unit
) {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(VetraTheme.colors.overlay)
            .clickable(onClick = onDismiss),
        contentAlignment = Alignment.Center
    ) {
        Box(
            modifier = Modifier
                .widthIn(max = 400.dp)
                .vetraShadow(
                    elevation = VetraTheme.shadows.xl,
                    shape = VetraTheme.shapes.xl
                )
                .background(VetraTheme.colors.canvasElevated, VetraTheme.shapes.xl)
                .padding(24.dp)
        ) {
            Column(
                verticalArrangement = Arrangement.spacedBy(16.dp)
            ) {
                Text(
                    text = "Confirm Action",
                    style = VetraTheme.typography.headingLg
                )
                Text(
                    text = "Are you sure you want to continue?",
                    style = VetraTheme.typography.bodyMd
                )
                Row(
                    modifier = Modifier.fillMaxWidth(),
                    horizontalArrangement = Arrangement.spacedBy(8.dp)
                ) {
                    VetraOutlinedButton(
                        onClick = onDismiss,
                        modifier = Modifier.weight(1f)
                    ) {
                        Text("Cancel")
                    }
                    VetraButton(
                        onClick = onConfirm,
                        modifier = Modifier.weight(1f)
                    ) {
                        Text("Confirm")
                    }
                }
            }
        }
    }
}

Design Principles

1. Consistency

Use predefined levels to ensure uniform visual hierarchy across your app.

2. Progressive Scale

The elevation gradually increases from xs (1dp) to xxl (24dp), creating natural depth perception.

3. Clear Visibility

Properly tuned alpha values (12-18% light, 24-32% dark) ensure shadows are clearly visible in both modes.

4. Adaptive Design

Different configurations for light and dark modes ensure optimal visibility.

5. Natural Feel

Mimics real-world lighting with heavier shadows below, creating a sense of light from above.

6. Cross-Platform

Consistent appearance across Android, iOS, Desktop, and Web platforms.

Technical Implementation

The adaptive shadow system features:

  1. Mode-Aware Configuration:
  2. Light Mode: Ambient 15%, Spot 25%, Multiplier 1.15×
  3. Dark Mode: Ambient 25%, Spot 35%, Multiplier 1.2×

  4. Diffusion Coefficient:

  5. Actual spread = elevation × multiplier
  6. Provides natural, controlled shadow spread

  7. Dual Shadow System:

  8. Ambient shadow: Soft base for overall depth
  9. Spot shadow: Directional for clear separation

  10. Natural Lighting Model:

  11. Mimics light from above
  12. Heavier shadows at bottom for realism

  13. Performance Optimized:

  14. Conditional application (0dp = no shadow)
  15. Single composition pass
  16. Efficient theme value access

Component Default Elevations

Component Default Elevation Reasoning
Button sm (2dp) Subtle, clickable
Card sm (2dp) Clear separation
Elevated Card md (4dp) More prominent
Menu lg (8dp) Floats above content
Dialog xl (16dp) Modal emphasis
Tooltip lg (8dp) Floats, readable
Bottom Sheet xl (16dp) Modal surface
FAB lg (8dp) Floating, accessible

Guidelines

Hierarchy Rules

Create clear hierarchy through elevation:

Column {
    // Background - no elevation
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(VetraTheme.colors.canvas)
    )

    // Card - light elevation
    VetraCard(
        modifier = Modifier
            .vetraShadow(elevation = VetraTheme.shadows.sm)
    ) {
        Text("Content")
    }

    // Floating menu - strong elevation
    VetraMenu(
        modifier = Modifier
            .vetraShadow(elevation = VetraTheme.shadows.lg)
    ) {
        MenuItem("Option")
    }

    // Dialog - maximum elevation
    VetraDialog(
        modifier = Modifier
            .vetraShadow(elevation = VetraTheme.shadows.xl)
    ) {
        Text("Modal")
    }
}

Avoid Elevation Conflicts

Don't place high elevation items inside lower elevation containers:

// ❌ Bad: Dialog (xl) inside Card (sm)
VetraCard {
    VetraDialog { } // Breaks hierarchy
}

// ✅ Good: Proper layering
Box {
    VetraCard { /* Content */ }
    if (showDialog) {
        VetraDialog { /* Modal */ }
    }
}

Best Practices

✅ Do

  • Use predefined elevation levels
  • Follow the elevation hierarchy
  • Let components use default elevations
  • Animate elevation changes smoothly
  • Test in both light and dark modes
  • Consider performance on lower-end devices

❌ Don't

  • Create arbitrary elevation values
  • Stack high elevation over higher elevation
  • Apply excessive shadows
  • Use shadows for decorative purposes only
  • Ignore the adaptive shadow system
  • Over-elevate standard content

Accessibility

Elevation doesn't directly impact accessibility, but:

  • Color Contrast: Ensure elevated surfaces maintain proper contrast
  • Focus Indicators: Elevation should not obscure focus rings
  • Motion: Reduce elevation animations if motion is reduced
// Ensure content on elevated surfaces is accessible
Box(
    modifier = Modifier
        .vetraShadow(elevation = VetraTheme.shadows.md)
        .background(VetraTheme.colors.canvasElevated)
) {
    Text(
        text = "Accessible text",
        color = VetraTheme.colors.textPrimary // Good contrast
    )
}

Migration Guide

From Standard Shadows

// Old
Modifier.shadow(elevation = 4.dp, shape = shape)

// New
Modifier.vetraShadow(elevation = VetraTheme.shadows.md, shape = shape)

Elevation Mapping

  • 0dp → shadows.none (0dp)
  • 1dp → shadows.xs (1dp)
  • 2-3dp → shadows.sm (2dp)
  • 4-6dp → shadows.md (4dp)
  • 8-12dp → shadows.lg (8dp)
  • 16-20dp → shadows.xl (16dp)
  • 24dp+ → shadows.xxl (24dp)

See Also: