Overview
Layers control the rendering order (z-index) of elements in Freya. Elements in higher layers are rendered on top of those in lower layers, allowing you to create overlays, modals, tooltips, and other UI that needs to appear above other content.
Think of layers like z-index in CSS - they determine which elements appear on top when elements overlap.
How Layers Work
Every element in Freya has a layer value that determines its rendering order:
Higher layer values render on top
Lower layer values render underneath
Elements in the same layer have no guaranteed order
use freya :: prelude ::* ;
fn app () -> impl IntoElement {
rect ()
. child (
rect ()
. background (( 255 , 0 , 0 ))
. layer ( - 1 ) // Renders underneath
)
. child (
rect ()
. background (( 0 , 255 , 0 ))
// Default layer (0)
)
. child (
rect ()
. background (( 0 , 0 , 255 ))
. layer ( 1 ) // Renders on top
)
}
Layer Types
Freya provides three layer variants:
Relative Layer::Relative(i16) - Offset from parent’s layer
Overlay Layer::Overlay - Jump to high layer for modals
RelativeOverlay Layer::RelativeOverlay(u8) - Multiple overlay levels
Relative Layers
The default layer type. Values are relative to the parent element’s layer:
use freya :: prelude ::* ;
fn app () -> impl IntoElement {
rect () // Layer 0 (default)
. child (
rect ()
. layer ( - 1 ) // Layer -1 (0 + (-1))
. child ( "Background" )
)
. child (
rect ()
// Layer 0 (0 + 0)
. child ( "Content" )
)
. child (
rect ()
. layer ( 1 ) // Layer 1 (0 + 1)
. child ( "Foreground" )
)
}
Use small positive/negative numbers for Relative layers (like -1, 0, 1) to keep your layer hierarchy manageable.
Overlay Layer
Use Layer::Overlay to jump to a very high layer - perfect for modals and dialogs:
use freya :: prelude ::* ;
#[derive( PartialEq )]
struct Modal ;
impl Component for Modal {
fn render ( & self ) -> impl IntoElement {
rect ()
. layer ( Layer :: Overlay ) // Renders above everything
. width ( Size :: fill ())
. height ( Size :: fill ())
. background (( 0 , 0 , 0 , 180 )) // Semi-transparent backdrop
. center ()
. child (
rect ()
. width ( Size :: px ( 400 . ))
. height ( Size :: px ( 300 . ))
. background (( 255 , 255 , 255 ))
. corner_radius ( CornerRadius :: new ( 8 . ))
. child ( "Modal content" )
)
}
}
RelativeOverlay Layers
For fine-grained control over multiple overlays:
use freya :: prelude ::* ;
fn app () -> impl IntoElement {
rect ()
. child (
rect ()
. layer ( Layer :: RelativeOverlay ( 0 )) // First overlay level
. child ( "Modal backdrop" )
)
. child (
rect ()
. layer ( Layer :: RelativeOverlay ( 1 )) // Second overlay level
. child ( "Tooltip on modal" )
)
. child (
rect ()
. layer ( Layer :: RelativeOverlay ( 2 )) // Third overlay level
. child ( "Context menu on tooltip" )
)
}
Common Use Cases
Ensure dropdown content appears above other elements:
use freya :: prelude ::* ;
#[derive( PartialEq )]
struct Dropdown ;
impl Component for Dropdown {
fn render ( & self ) -> impl IntoElement {
let mut open = use_state ( || false );
rect ()
. child (
Button :: new ()
. on_press ( move | _ | open . set ( ! open . read ()))
. child ( "Open Menu" )
)
. child (
if * open . read () {
rect ()
. layer ( Layer :: Overlay ) // Appears above everything
. background (( 255 , 255 , 255 ))
. border ( Border :: all (( 1 . , ( 200 , 200 , 200 , 255 ))))
. corner_radius ( CornerRadius :: new ( 4 . ))
. padding ( Gaps :: new_all ( 8 . ))
. child ( "Menu Item 1" )
. child ( "Menu Item 2" )
. child ( "Menu Item 3" )
} else {
rect () // Empty when closed
}
)
}
}
Show tooltips above content:
use freya :: prelude ::* ;
#[derive( PartialEq )]
struct TooltipButton ;
impl Component for TooltipButton {
fn render ( & self ) -> impl IntoElement {
let mut hover = use_state ( || false );
rect ()
. child (
rect ()
. on_mouse_over ( move | _ | hover . set ( true ))
. on_mouse_leave ( move | _ | hover . set ( false ))
. background (( 100 , 100 , 255 ))
. padding ( Gaps :: new_all ( 12 . ))
. child ( "Hover me" )
)
. child (
if * hover . read () {
rect ()
. layer ( Layer :: Overlay )
. background (( 0 , 0 , 0 , 230 ))
. color (( 255 , 255 , 255 ))
. padding ( Gaps :: new_all ( 8 . ))
. corner_radius ( CornerRadius :: new ( 4 . ))
. child ( "This is a tooltip" )
} else {
rect ()
}
)
}
}
Loading Overlay
Show loading state over entire app:
use freya :: prelude ::* ;
#[derive( PartialEq )]
struct App ;
impl Component for App {
fn render ( & self ) -> impl IntoElement {
let loading = use_state ( || false );
rect ()
// Main content
. child (
rect ()
. expanded ()
. child ( "Main app content" )
)
// Loading overlay
. child (
if * loading . read () {
rect ()
. layer ( Layer :: Overlay )
. width ( Size :: fill ())
. height ( Size :: fill ())
. background (( 0 , 0 , 0 , 150 ))
. center ()
. child (
rect ()
. background (( 255 , 255 , 255 ))
. padding ( Gaps :: new_all ( 20 . ))
. corner_radius ( CornerRadius :: new ( 8 . ))
. child ( "Loading..." )
)
} else {
rect ()
}
)
}
}
Notification Toast
Show temporary notifications:
use freya :: prelude ::* ;
#[derive( PartialEq )]
struct Toast {
message : String ,
}
impl Component for Toast {
fn render ( & self ) -> impl IntoElement {
rect ()
. layer ( Layer :: RelativeOverlay ( 1 )) // Above modals
. width ( Size :: px ( 300 . ))
. background (( 40 , 40 , 40 ))
. color (( 255 , 255 , 255 ))
. padding ( Gaps :: new_all ( 16 . ))
. corner_radius ( CornerRadius :: new ( 8 . ))
. shadow (( 0 . , 4 . , 12 . , 0 . , ( 0 , 0 , 0 , 100 )))
. child ( & self . message)
}
}
Layer Hierarchy Example
Here’s a complete example showing multiple layer levels:
use freya :: prelude ::* ;
fn main () {
launch ( LaunchConfig :: new () . with_window ( WindowConfig :: new ( app )))
}
fn app () -> impl IntoElement {
let mut show_modal = use_state ( || false );
let mut show_tooltip = use_state ( || false );
rect ()
. expanded ()
// Background pattern (layer -1)
. child (
rect ()
. layer ( - 1 )
. width ( Size :: fill ())
. height ( Size :: fill ())
. background (( 240 , 240 , 240 ))
. child ( "Background" )
)
// Main content (layer 0)
. child (
rect ()
. center ()
. child (
Button :: new ()
. on_press ( move | _ | show_modal . set ( true ))
. child ( "Open Modal" )
)
)
// Floating action button (layer 1)
. child (
rect ()
. layer ( 1 )
. width ( Size :: px ( 60 . ))
. height ( Size :: px ( 60 . ))
. background (( 255 , 100 , 100 ))
. corner_radius ( CornerRadius :: new ( 30 . ))
. center ()
. on_mouse_over ( move | _ | show_tooltip . set ( true ))
. on_mouse_leave ( move | _ | show_tooltip . set ( false ))
. child ( "+" )
)
// Tooltip (Overlay)
. child (
if * show_tooltip . read () {
rect ()
. layer ( Layer :: Overlay )
. background (( 0 , 0 , 0 , 230 ))
. color (( 255 , 255 , 255 ))
. padding ( Gaps :: new_all ( 8 . ))
. corner_radius ( CornerRadius :: new ( 4 . ))
. child ( "Add new item" )
} else {
rect ()
}
)
// Modal (Overlay)
. child (
if * show_modal . read () {
rect ()
. layer ( Layer :: Overlay )
. width ( Size :: fill ())
. height ( Size :: fill ())
. background (( 0 , 0 , 0 , 180 ))
. center ()
. on_mouse_up ( move | _ | show_modal . set ( false ))
. child (
rect ()
. width ( Size :: px ( 400 . ))
. height ( Size :: px ( 300 . ))
. background (( 255 , 255 , 255 ))
. corner_radius ( CornerRadius :: new ( 12 . ))
. padding ( Gaps :: new_all ( 20 . ))
. child ( "Modal Content" )
)
} else {
rect ()
}
)
}
Best Practices
Use Overlay for modals and dialogs
Always use Layer::Overlay for UI that must appear above everything: rect ()
. layer ( Layer :: Overlay )
. child ( "Modal content" )
Keep relative layers simple
Use small offsets (-1, 0, 1, 2) instead of large numbers: // ✅ Good
rect () . layer ( - 1 ) // Background
rect () // Default
rect () . layer ( 1 ) // Foreground
// ❌ Avoid
rect () . layer ( - 100 )
rect () . layer ( 999 )
Elements in same layer are unordered
Don’t rely on render order within the same layer: // ❌ Bad - both at layer 0, order not guaranteed
rect () . child ( rect () . background ( Color :: RED ))
. child ( rect () . background ( Color :: BLUE ))
// ✅ Good - explicit layers
rect () . child ( rect () . layer ( 0 ) . background ( Color :: RED ))
. child ( rect () . layer ( 1 ) . background ( Color :: BLUE ))
Use RelativeOverlay for stacked overlays
When you have multiple overlay levels: rect ()
. layer ( Layer :: RelativeOverlay ( 0 )) // Base modal
. child (
rect ()
. layer ( Layer :: RelativeOverlay ( 1 )) // Dropdown on modal
)
Common Patterns
Portal Pattern
Render content at a specific layer regardless of component hierarchy:
use freya :: prelude ::* ;
#[derive( PartialEq )]
struct Portal {
content : Element ,
}
impl Component for Portal {
fn render ( & self ) -> impl IntoElement {
rect ()
. layer ( Layer :: Overlay )
. child ( self . content . clone ())
}
}
Z-Index Stack
Manage multiple layers systematically:
use freya :: prelude ::* ;
// Define layer constants
const BACKGROUND : i16 = - 1 ;
const CONTENT : i16 = 0 ;
const SIDEBAR : i16 = 1 ;
const DROPDOWN : i16 = 2 ;
fn app () -> impl IntoElement {
rect ()
. child ( rect () . layer ( BACKGROUND ))
. child ( rect () . layer ( CONTENT ))
. child ( rect () . layer ( SIDEBAR ))
. child ( rect () . layer ( DROPDOWN ))
}
Debugging Layers
Visually debug layer order:
fn debug_layers () -> impl IntoElement {
rect ()
. child (
rect ()
. layer ( - 1 )
. background (( 255 , 0 , 0 , 100 ))
. child ( "Layer -1" )
)
. child (
rect ()
. background (( 0 , 255 , 0 , 100 ))
. child ( "Layer 0" )
)
. child (
rect ()
. layer ( 1 )
. background (( 0 , 0 , 255 , 100 ))
. child ( "Layer 1" )
)
. child (
rect ()
. layer ( Layer :: Overlay )
. background (( 255 , 255 , 0 , 100 ))
. child ( "Overlay" )
)
}
Next Steps
Elements Learn about the core elements
Components Build layered components
Events Handle events across layers
Portal Component Use the Portal component