ZenitUI
A retained-mode UI framework for games and tools, with a CSS-like styling system, a Flexbox-inspired layout engine, and an optional declarative DSL (ZMarkup) for building interfaces without writing C++ boilerplate.
Status: 🗓️ beta — the core API is frozen. See ROADMAP for what's still planned.
What is ZenitUI?
ZenitUI is a retained-mode UI framework written in modern C++.
It gives you the ergonomics of a web-like styling and layout system
(Flexbox, CSS transitions, @keyframes, ::part, var(), calc())
without pulling in a browser engine or a scripting runtime.
You write your UI in one of two ways (or both, mixed):
1. Imperative C++ — with a fluent API
auto root = std::make_shared<Layout>(LayoutType::Stack);
root->size(Percent(100), Percent(100));
root->getInlineBase().itemsH = Align::Center;
root->getInlineBase().itemsV = Align::Center;
auto btn = Btn("Click me", []{
std::printf("hello!\n");
});
btn->cls("btn-primary");
root->addChild(btn);
2. Declarative — with ZMarkup + ZStyle
/* game.zstyle */
.btn-primary {
background: #0079F1;
radius: 8px;
padding: 1.5vh 2vw;
transition: background 0.25s ease-out-quad, scale 0.12s ease-out-back;
}
.btn-primary:hover { background: #3296FF; scale: 1.15; }
.btn-primary:pressed { background: #505050; scale: 0.90; }
// C++
auto ui = ZMarkup::build(R"(
VStack#root {
Button.btn-primary#go "Click me"
}
)");
ui.onClick("go", []{ std::printf("hello!\n"); });
Both approaches produce the same tree of Layout nodes, and both are
fully compatible with the same styling system.
Feature highlights
| Area | What you get |
|---|---|
| Layout | Stack, Vertical, Horizontal flow, grow / shrink / gap, position: absolute, overflow: scroll \| auto, z-index, portals |
| Units | px, %, vw, vh, pw, ph, auto, full calc() support |
| Styling | CSS-like cascade, descendant selectors, :hover :pressed :focus :checked :disabled, ::part, custom properties (--x / var()), media queries |
| Animations | Per-property transitions (transition: opacity 0.3s ease-out), CSS @keyframes, imperative UIAnimation, per-::part animations |
| Input | Hover / pressed / focus state machine, Tab navigation, pointer capture, passThrough, right-click, context menus |
| Effects | filter: blur(...) drop-shadow(...), arbitrary shaders, extensible FilterRegistry, box-shadow |
| Backend | Pluggable. Reference implementation ships for Raylib, but the core only depends on IRenderer / IPlatform / IAssetProvider |
| Debugging | Optional Debug::dumpStyle / dumpTree / dumpTheme / dumpParts, structured Logger with source locations |
| Testing | Headless MockRenderer + MockPlatform allow full interaction tests without opening a window |
Quick start
#include <raylib.h>
#include "RaylibBackend.hpp"
#include "UI.hpp"
using namespace ZenitUI;
int main() {
InitWindow(800, 600, "ZenitUI");
RaylibRenderer renderer;
RaylibPlatform platform;
RaylibAssetProvider assets(renderer);
UIContext::get().renderer = &renderer;
UIContext::get().platform = &platform;
UIContext::get().assets = &assets;
auto root = std::make_shared<Layout>(LayoutType::Stack);
root->size(Percent(100), Percent(100));
root->addChild(Btn("Hello!", []{ std::printf("hi\n"); }));
while (!WindowShouldClose()) {
float dt = GetFrameTime();
root->runFrame(dt); // input, layout, animation
BeginDrawing();
ClearBackground(BLACK);
root->renderFrame();
EndDrawing();
}
CloseWindow();
}
See Getting Started for a full walkthrough.
Documentation map
For users (writing UI)
- Getting Started — set up the backend, first layout, first widget
- Layout — Stack / Vertical / Horizontal, flexbox, units
- Widgets — reference for every built-in widget
- ZMarkup — the declarative DSL
- ZStyle — the
.zstylestyling language - Events — click, hover, focus, bubbling, pointer capture
- Animations — transitions and keyframes
- Portals — Popup, Tooltip, Dropdown, Modal
- Effects — filters, shaders,
FilterRegistry - Custom Widgets — build your own component
For contributors (extending the core)
- Architecture — high-level view, the frame pipeline
- Lifecycle —
update/measure/arrange/draw - Style System — cascade, specificity, inheritance
- Animation System — imperative vs CSS vs
::part - Layout Engine — intrinsic size, flow, dirty tracking
- Render Pipeline — inline vs layer, filters, portals
- Input System — FSM, focus, pointer capture
- Scroll System —
ScrollState, arrange, inertia - Backend — implementing
IRenderer/IPlatform - Debugging — dumps, logging, headless tests
- Benchmarks — performance tracking and the CI chart
API reference
Repository layout (overview)
include/
Common.hpp CoreTypes.hpp Unit.hpp
Layout.hpp Style.hpp Theme.hpp
StyleResolver.hpp StyleParser.hpp StyleAttr.hpp
AnimationPlayer.hpp AnimPrimitives.hpp Easing.hpp
InputController.hpp ScrollController.hpp ScrollState.hpp
FilterRegistry.hpp UIContext.hpp Media.hpp
UI.hpp UIComponents.hpp UIEnums.hpp
Logger.hpp Debug.hpp
ZMarkup.hpp
components/ Button, Toggle, Slider, Checkbox, TextInput,
ProgressBar, Dropdown, ScrollView, Modal,
Tooltip, Popup, Canvas, Text, ImageContainer
src/
Layout.cpp Measure.cpp Render.cpp
StyleResolver.cpp AnimationPlayer.cpp Input.cpp
ScrollController.cpp FilterRegistry.cpp
backends/
raylib/ RaylibBackend.{hpp,cpp}
tests/
Mocks.hpp test_*.cpp
License
To be defined.