A retained-mode UI framework for C++ with a CSS-like styling system, a Flexbox-inspired layout engine, and a declarative DSL (ZMarkup + ZStyle) for building interfaces without writing boilerplate.
Status: ποΈ beta β the core API is frozen. See docs/ROADMAP.md for what's still planned.
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 freely:
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
auto ui = ZMarkup::build(R"(
VStack#root {
Button.btn-primary#ok "Click me"
}
)");
ui.onClick("ok", []{ std::printf("hello!\n"); });/* assets/game.zstyle */
.btn-primary {
background: #0079F1;
radius: 8px;
padding: 1.5vh 2vw;
transition: background 0.25s ease-out, scale 0.12s ease-out-back;
}
.btn-primary:hover { background: #3296FF; scale: 1.15; }
.btn-primary:pressed { background: #505050; scale: 0.90; }Both approaches produce the same tree of Layout nodes, and both are
fully compatible with the same styling system.
| Area | What you get |
|---|---|
| Layout | Stack, Vertical, Horizontal flow, grow / shrink / gap, absolute positioning, overflow: scroll | auto, z-index, portals |
| Units | px, %, vw, vh, pw, ph, auto, and 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, 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 |
| Widgets | Text, Button, Panel, Toggle, Checkbox, Slider, ProgressBar, TextInput, ScrollView, Dropdown, Modal, Tooltip, Popup, Canvas, ImageContainer |
| Backend | Pluggable. Reference implementation ships for Raylib. The core only depends on IRenderer / IPlatform / IAssetProvider. |
| Debugging | Optional Debug::dumpStyle / dumpTree / dumpTheme / dumpParts, structured Logger with source locations |
| Data binding | Bindable<T> β observable values with RAII subscriptions, lifetime-safe, zero dependencies |
| Testing | Headless MockRenderer + MockPlatform allow full interaction tests without opening a window |
#include <raylib.h>
#include "RaylibBackend.hpp"
#include "UI.hpp"
using namespace ZenitUI;
int main() {
InitWindow(800, 600, "ZenitUI");
SetWindowState(FLAG_WINDOW_RESIZABLE);
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->getInlineBase().itemsH = Align::Center;
root->getInlineBase().itemsV = Align::Center;
root->addChild(Btn("Hello!", []{ std::printf("hi\n"); }));
while (!WindowShouldClose()) {
root->runFrame(GetFrameTime());
BeginDrawing();
ClearBackground(BLACK);
root->renderFrame();
EndDrawing();
}
CloseWindow();
}A full walkthrough is in Getting Started.
The project is built with a plain Makefile. It expects:
- A C++20 compiler (
g++by default). - Raylib unpacked in
deps/raylib/, with:- headers in
deps/raylib/include/ - libraries in
deps/raylib/lib/
- headers in
make # builds ./bin/testui (the demo app)
make test # builds and runs the headless test suite
make clean # removes build artifacts
make all # main + testThe current Makefile targets MinGW / GCC on Windows (it links
-lopengl32 -lgdi32 -lwinmm). On Linux and macOS, the same targets work
if Raylib is present and the link flags are adjusted for the platform
(-lGL -lm -lpthread -ldl -lrt -lX11 on Linux, no extra flags on
macOS).
The test suite is fully portable β it doesn't link Raylib at all.
On Linux and macOS, make test works out of the box.
A CMake build for full cross-platform support (including MSVC) is planned β see docs/ROADMAP.md.
The full documentation lives in docs/:
- Getting Started β setup, first layout, first widget
- Cheatsheet β one-page reference for day-to-day work
- Glossary β terminology used across the framework
- 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
- Data Binding β
Bindable<T>for model β UI
- 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
LayoutΒ·WidgetsΒ·UnitΒ·StyleΒ·ThemeΒ·ZMarkupΒ·ZStyleΒ·FilterRegistryΒ·UIContextΒ·Bindable
src/
pch.hpp Precompiled header (Raylib app only)
main.cpp Demo entry point
ui/
Layout.hpp Core node type
Style.hpp Style / ComputedStyle
Theme.hpp Rules, keyframes, specificity
StyleResolver.hpp Cascade + transitions
StyleParser.hpp .zstyle parser
StyleAttr.hpp Attribute value parsers
AnimationPlayer.hpp Imperative + CSS animation
AnimPrimitives.hpp Keyframe types, evaluators
Easing.hpp TransitionFunction
InputController.hpp Input FSM
ScrollController.hpp Scroll orchestration
ScrollState.hpp Scroll state (POD)
FilterRegistry.hpp Filter registration
UIContext.hpp The three backend interfaces + context
Media.hpp Media queries
CoreTypes.hpp Vec2, Rect, Color, ...
Common.hpp Standard includes
Logger.hpp Structured logging
Debug.hpp Debug dumps
UI.hpp Aggregator header
UIComponents.hpp Widget aggregator
UIEnums.hpp LayoutType, UIState
Unit.hpp Value / Unit
ZMarkup.hpp DSL parser + builder
components/ Widgets (header-only)
Text.hpp Button.hpp Panel.hpp ImageContainer.hpp
Toggle.hpp Checkbox.hpp Slider.hpp ProgressBar.hpp
TextInput.hpp ScrollView.hpp Dropdown.hpp
Canvas.hpp Modal.hpp Tooltip.hpp Popup.hpp
Layouts.hpp
layout/ Core implementation
Layout.cpp
Measure.cpp
Render.cpp
StyleResolver.cpp
AnimationPlayer.cpp
Input.cpp
ScrollController.cpp
FilterRegistry.cpp
backend/
RaylibBackend.hpp
RaylibBackend.cpp
deps/
raylib/ Vendored Raylib (include/, lib/)
tests/
TestFramework.hpp
Mocks.hpp
test_main.cpp
test_*.cpp
docs/
README.md CHEATSHEET.md GLOSSARY.md ROADMAP.md
user/ internals/ api/
assets/ .zstyle files, fonts, textures, shaders
See CONTRIBUTING.md.
ZenitUI is in beta. The core API is frozen: widgets, styling, layout, animations, events, and the backend interfaces described in the user guide and API reference are stable. Breaking changes are still possible for work that lands in Phases 4β8 of the roadmap (performance, feature completion, tooling), but the surface described in Phases 1β3 will not change.
See docs/ROADMAP.md for the detailed plan from alpha to beta to 1.0, including known issues and planned features.
MIT Β© 2026 zLouis043