Dev.to · 6 min read

I Built CSS-Like Native UI Animations with Nim and SDL3

I Built CSS-Like Native UI Animations with Nim and SDL3

Can CSS-like keyframe animations and hover transitions work in a native GUI without a browser or WebView? Clay Board Style System v0.4.1, released on August 12, 2026, includes a new SDL3 demo that shows them running in a native UI built with Nim. Select the preview to open the full MP4 recording. Clay Board Style System is abbreviated as CBSS throughout the rest of this article. Why CSS-like animation is useful in native GUI development The first version of a native UI animation often starts with a timer and a mutable position: every frame -> calculate elapsed time -> interpolate a value -> update the widget -> rebuild what changed -> request another frame That is manageable for one object. It becomes harder when one component combines a hover transition, several named animations, delays, easing functions, alternating directions, cancellation, and reduced-motion behavior. The host application also needs to distinguish work that changes layout from work that changes only presentation. Moving a box with a paint transform should not require the complete style and layout pipeline to run again on every frame. CBSS addresses this by keeping motion declarations in the style model and sampling only active tracks when their next frame deadline arrives. What the Clay Board Style System v0.4.1 animation demo shows The declarative motion runtime itself landed in v0.4.0 on August 11. Version 0.4.1 is a smaller, intentionally focused release: it adds a polished example, GIF and MP4 recordings, and roadmap documentation for exposing declarative motion through the C ABI in Version 0.5 and later. The new demo makes four behaviors visible in one native SDL3 window: automatic horizontal travel with eased keyframes; an in-place 2D flip expressed with a typed transform; background color, text color, and opacity animations sharing one retained node; a reversible hover transition for color, opacity, translation, scale, and rotation. This matters because an API can look complete in unit tests while still being awkward to compose. The demo exercises multiple tracks, negative delays, alternate directions, inherited text color, hover state changes, paint, hit testing, and the SDL3 host loop together. Several objects moving smoothly at once is not unusual by itself; mature UI frameworks already do that. The purpose of this demo is to show that CSS-inspired keyframes and transitions can compose inside one retained native UI model, using typed Nim declarations and an SDL3 event loop. Writing native keyframe animation without a manual interpolation loop The horizontal movement in the demo is registered as named style keyframes. The application describes the values at four offsets; the motion runtime owns timing and interpolation between them. ui.registerStyleKeyframes(styleKeyframes("ease-shuttle", [ styleKeyframe(0, [ decl("transform", transformValue(translate(px(0), px(0)))) ]), styleKeyframe(0.12, [ decl("transform", transformValue(translate(px(0), px(0)))) ]), styleKeyframe(0.88, [ decl("transform", transformValue(translate(px(350), px(0)))) ]), styleKeyframe(1, [ decl("transform", transformValue(translate(px(350), px(0)))) ]) ])) The component then associates the animation name with its duration, timing function, iteration count, direction, and fill mode: uiStyle([ animationNames("ease-shuttle"), animationDurations(2.6'f32), animationTimingFunctions("cubic-bezier(0.42, 0, 0.58, 1)"), decl("animation-iteration-count", keyword("infinite")), animationDirection(adAlternate), animationFillMode(afBoth) ]) The syntax is CSS-inspired, but the values are authored through typed Nim helpers. There is no DOM, WebView, JavaScript animation loop, or string selector involved. Running multiple CSS-like animations on one native UI node The color panel demonstrates a case that is easy to underestimate: two animations run on the same node with different durations and delays. uiStyle([ animationNames("palette-cycle", "opacity-breathe"), animationDurations(6.2'f32, 3.1'f32), animationDelays(0.0'f32, -0.9'f32), animationTimingFunctions("ease-in-out", "ease-in-out"), decl("animation-iteration-count", keyword("infinite, infinite")), animationDirections(adAlternate, adNormal), animationFillModes(afBoth, afBoth) ]) CBSS keeps the tracks independent and applies declaration order when animated values overlap. CSS-like list cycling is supported for animation names, durations, delays, timing functions, iteration counts, directions, fill modes, play states, and composition. Creating a reversible hover transition in a native GUI The interactive panel starts with normal style and adds a hover style. The transition declarations describe how the runtime moves between the two states. transitionProperties("background-color", "opacity", "transform") transitionDurations(0.42'f32, 0.24'f32, 0.52'f32) transitionTimingFunctions( "ease-out", "ease-out", "cubic-bezier(0.16, 1, 0.3, 1)" ) When the pointer enters or leaves the button, CBSS resolves the new state and reconciles the transition once. Active samples then update paint data and, while geometry changes, hit-test data. Reversing the hover state starts from the current presented value rather than visibly jumping to an endpoint. How the SDL3 animation loop stays event-driven Declarative motion does not mean rendering continuously when nothing is active. The scheduler computes the next deadline and lets the SDL3 event loop wait until either an input/window event arrives or that deadline is due. state or input event -> resolve style and reconcile motion once -> schedule the next active-track deadline deadline arrives -> sample active transitions and keyframes -> refresh paint and, if required, hit data -> render -> wait for the next event or deadline The important boundary is between layout and presentation. The demo's transforms, colors, and opacity can be sampled through paint and hit updates without running style resolution or layout on every animation frame. Layout still runs when a real layout-affecting change, such as a resize, makes it necessary. Run the Nim and SDL3 native UI animation demo The reproducible source for the recording is included in the v0.4.1 tag: git clone https://github.com/puffball1567/clay-board-style-system.git cd clay-board-style-system git checkout v0.4.1 nimble setupBundled nimble declarativeMotionDemo The current requirements are Nim 2.2 or newer plus Rust and Cargo for the native text and image bridges. Linux x86_64 with SDL3 remains the Tier 1 runtime target. Windows x86_64 and macOS arm64 are covered by the portable CI suite, but their complete native runtime paths still need contributor validation. The complete example is available in examples/declarative_motion_demo.nim. Current limits of CSS-like native UI animation in CBSS Version 0.4.1 remains a developer preview, so public APIs may change before 1.0. The current declarative motion path focuses on opacity, foreground and background colors, and typed 2D transforms. Additional values, filters, 3D transforms, and CPU effects remain roadmap work. Reduced-motion handling exists in the Nim runtime, while foreign-language keyframe registration and transition control through the C ABI are planned for Version 0.5 and later; v0.4.1 documents that boundary but does not claim it is already implemented. Release and demo timestamps GitHub lists v0.4.1 as a non-prerelease published at 2026-08-12 11:26:51 JST (02:26:51 UTC). The GIF and MP4 demo assets were uploaded to the same release during the 11:26 JST minute. Both URLs in this article are pinned to v0.4.1 rather than the moving main branch. Clay Board Style System v0.4.1 release Declarative motion demo source Project repository

This is a summary aggregated from Dev.to. Read the complete article on the original site:

Read full article at Dev.to

More Programming & Dev News