Skip to main content

DOM Model

lui maintains its own DOM-like tree in lui-tree. This is not a web DOM — there is no JavaScript, no document, no live NodeList. It is a Rust-native tree of elements.

Tree Structure

pub struct Tree {
pub root: Node,
pub fonts: FontDatabase,
pub interaction: InteractionState,
pub generation: u64,
pub dirty_paths: Vec<Vec<usize>>,
pub preload_queue: Vec<String>,
}

pub struct Node {
pub element: Element,
pub children: Vec<Node>,
pub on_click: Option<Callback>,
pub on_mouse_enter: Option<Callback>,
pub on_mouse_leave: Option<Callback>,
pub on_mouse_down: Option<Callback>,
pub on_mouse_up: Option<Callback>,
pub on_focus: Option<Callback>,
pub on_blur: Option<Callback>,
pub on_event: Option<TypedEventCallback>,
}

Element Types

Each HTML element type has its own Rust struct with typed attributes. ~100 element types are modeled, including:

  • Content: Div, P, Span, A, H1-H6, Pre, Br, Hr
  • Sections: Body, Header, Footer, Nav, Main, Section, Article, Aside
  • Forms: Input, Textarea, Button, Select, Option, Label, Fieldset
  • Tables: Table, Tr, Td, Th, Thead, Tbody, Tfoot
  • Media: Img, Video, Audio, Picture, Source
  • Lists: Ul, Ol, Li, Dl, Dt, Dd
  • Metadata: Head, Title, Meta, Link, Style, Script

Building the Tree

The tree is typically built via parsing:

let mut tree = lui_parser::parse(html_string);

It can also be constructed programmatically by building Node structures directly.

InteractionState

The tree carries all runtime interaction state:

pub struct InteractionState {
pub hover_path: Vec<usize>,
pub active_path: Vec<usize>,
pub focus_path: Option<Vec<usize>>,
pub focus_visible: bool,
pub text_selection: Option<TextSelection>,
pub scroll_offsets_y: BTreeMap<Vec<usize>, f32>,
pub scroll_offsets_x: BTreeMap<Vec<usize>, f32>,
pub modifiers: Modifiers,
pub mouse_buttons: u8,
pub time_origin: Instant,
}

Event Callbacks

Elements carry optional Rust closures for interactivity:

// Legacy per-event-type callbacks
node.on_click = Some(Box::new(|| { println!("clicked!"); }));

// Typed event dispatch
node.on_event = Some(Box::new(|event: &HtmlEvent| {
match &event.event_type {
HtmlEventType::Click(_) => { /* ... */ }
_ => {}
}
}));

Query Selectors

DOM-style element lookup:

tree.get_element_by_id("my-id");
tree.get_elements_by_class_name("my-class");
tree.query_selector("div.container > p");
tree.query_selector_all("input[type=text]:focus");

The query engine supports full CSS Level 4 selectors: all combinators, attribute selectors, pseudo-classes (:is(), :where(), :not(), :has(), :nth-child(), etc.).

Node Paths

Every node in the tree is identified by a Vec<usize> path — the sequence of child indices from root to the target node. Paths are used for:

  • Hit-testing (returning the deepest matching path)
  • Event dispatch (targeting the correct element)
  • Focus tracking (storing the focused element's path)
  • Scroll offset storage (per-element scroll positions)