Skip to content

Transitions & Hover States ​

RTXUI supports CSS-style transitions and interactive pseudo-classes on DOM elements. This allows you to build terminal applications with smooth color shifts, expanding borders, and responsive mouse effects without writing manual frame updates.


1. Interactive Pseudo-Classes ​

You can define styles that apply only under specific user interaction states:

  • :hover: Active when the mouse cursor is hovering over the element or its children.
  • :active: Active while the element is being clicked/pressed.
  • :focus: Active when the element has captured keyboard focus.

Example Rulesets ​

css
.button {
  background-color: rgb(30, 58, 138); /* Slate Blue */
}
.button:hover {
  background-color: rgb(29, 78, 216); /* Bright Blue on hover */
}
.button:active {
  background-color: rgb(30, 64, 175); /* Dark Blue on click */
}

Minimal Interactive Demo ​

Below is the interactive tab view for :hover, :active, and :focus pseudo-classes:

Fullscreen Demo
cpp
// Copyright 2026 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
//
// The interactive pseudo-classes: :hover, :focus and :active.
//
// Rules can be nested inside their parent with `&`, exactly as in modern CSS,
// so an element's interactive states live next to its base declarations.
//
// Try it: move the mouse over the buttons, Tab between them, and hold the
// mouse button down to see all three states.
#include <rtxui/rtxui.hpp>

using namespace rtxui;

class PseudoClassesDemo : public Component<PseudoClassesDemo> {
 public:
  std::string_view view = R"html(
      <div class="card">
        <h1>Pseudo-Classes</h1>
        <p>Hover, Tab to focus, or hold down the mouse button.</p>

        <div class="row">
          <div class="btn" tabindex="0">Hover me</div>
          <div class="btn" tabindex="0">Focus me</div>
          <div class="btn" tabindex="0">Press me</div>
        </div>

        <div class="legend">
          <span class="swatch hover"> </span><span>:hover</span>
          <span class="swatch focus"> </span><span>:focus</span>
          <span class="swatch active"> </span><span>:active</span>
        </div>
      </div>

      <style>
        self {
          --bg: rgb(13, 17, 23);
          --border: rgb(48, 54, 61);
          --text: rgb(230, 237, 243);
          --muted: rgb(139, 148, 158);
          --accent: rgb(88, 166, 255);
          --focus: rgb(163, 113, 247);
          --active: rgb(63, 185, 80);

          display: flex;
          align-items: center;
          justify-content: center;
          width: 100%;
          height: 100%;
          background-color: var(--bg);
          color: var(--text);
        }
        .card {
          border: tall;
          border-color: var(--border);
          background-color: rgb(22, 27, 34);
          padding: 1 3;
        }
        h1 {
          color: var(--accent);
          font-weight: bold;
        }
        p {
          color: var(--muted);
          margin-bottom: 1;
        }
        .row {
          display: flex;
          gap: 2;
          margin-bottom: 1;
        }
        .btn {
          border: tall;
          border-color: var(--border);
          background-color: var(--bg);
          color: var(--text);
          padding: 0 2;
          text-align: center;
          transition: background-color 0.2s ease, border-color 0.2s ease,
                      color 0.2s ease;

          &:hover {
            border-color: var(--accent);
            color: var(--accent);
          }

          &:focus {
            border-color: var(--focus);
            color: var(--focus);
          }

          &:active {
            background-color: var(--active);
            border-color: var(--active);
            color: var(--bg);
          }
        }
        .legend {
          display: flex;
          gap: 1;
          color: var(--muted);
        }
        .swatch {
          width: 2;
        }
        .swatch.hover { background-color: var(--accent); }
        .swatch.focus { background-color: var(--focus); }
        .swatch.active { background-color: var(--active); }
      </style>
    )html";
};

int main() {
  auto app = Ref<PseudoClassesDemo>::New();
  Screen screen(app);
  screen.Loop();
  return 0;
}

2. CSS Transitions ​

The transition property configures how style attributes interpolate smoothly over time instead of changing instantly.

Shorthand Syntax ​

css
transition: <property> <duration> [<timing-function>] [<delay>]

You can declare multiple property transitions by separating them with commas, or use the keyword all to animate all supported variables:

css
transition: background-color 0.2s ease-in-out, width 0.3s ease-out;

Animatable Properties ​

  • Dimensions: width, height (e.g. 20 to 40 cells).
  • Borders: border-top-color, border-right-color, border-bottom-color, border-left-color (or shorthand border-color).
  • Colors & Opacities: color, background-color, opacity.
  • Flex Constraints: flex-grow, flex-shrink.

Easing Functions ​

  • linear
  • ease (Default curve)
  • ease-in
  • ease-out
  • ease-in-out
  • Sine curves: ease-in-sine, ease-out-sine, ease-in-out-sine
  • Quad curves: ease-in-quad, ease-out-quad, ease-in-out-quad
  • Cubic curves: ease-in-cubic, ease-out-cubic, ease-in-out-cubic
  • Quart curves: ease-in-quart, ease-out-quart, ease-in-out-quart
  • Quint curves: ease-in-quint, ease-out-quint, ease-in-out-quint
  • Expo curves: ease-in-expo, ease-out-expo, ease-in-out-expo
  • Circ curves: ease-in-circ, ease-out-circ, ease-in-out-circ
  • Back curves: ease-in-back, ease-out-back, ease-in-out-back
  • cubic-bezier(x1, y1, x2, y2) (Defines a custom cubic Bézier easing curve)

Minimal Transition Demo ​

Below is the interactive tab view for background-color and border-color transitions:

Fullscreen Demo
cpp
// Copyright 2026 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
//
// CSS transitions.
//
// `transition` interpolates a property between its old and new computed value
// whenever a rule stops or starts matching -- here, when :hover applies. Each
// property can carry its own duration and easing function.
//
// Try it: hover each card and watch them settle at different speeds.
#include <rtxui/rtxui.hpp>

using namespace rtxui;

class TransitionsDemo : public Component<TransitionsDemo> {
 public:
  std::string_view view = R"html(
      <div class="card">
        <h1>Transitions</h1>
        <p>Hover a tile. Each one eases on a different curve.</p>

        <div class="row">
          <div class="tile linear">
            <span class="name">linear</span>
            <span class="time">0.2s</span>
          </div>
          <div class="tile ease">
            <span class="name">ease-in-out</span>
            <span class="time">0.5s</span>
          </div>
          <div class="tile slow">
            <span class="name">ease-out</span>
            <span class="time">1.0s</span>
          </div>
        </div>
      </div>

      <style>
        self {
          --bg: rgb(13, 17, 23);
          --border: rgb(48, 54, 61);
          --muted: rgb(139, 148, 158);
          --accent: rgb(88, 166, 255);

          display: flex;
          align-items: center;
          justify-content: center;
          width: 100%;
          height: 100%;
          background-color: var(--bg);
          color: rgb(230, 237, 243);
        }
        .card {
          border: tall;
          border-color: var(--border);
          background-color: rgb(22, 27, 34);
          padding: 1 3;
        }
        h1 {
          color: var(--accent);
          font-weight: bold;
        }
        p {
          color: var(--muted);
          margin-bottom: 1;
        }
        .row {
          display: flex;
          gap: 2;
        }
        .tile {
          display: flex;
          flex-direction: column;
          border: tall;
          border-color: var(--border);
          background-color: var(--bg);
          color: var(--muted);
          padding: 1 2;
          width: 18;
          text-align: center;
        }
        .tile:hover {
          background-color: var(--accent);
          border-color: var(--accent);
          color: var(--bg);
        }
        .name {
          font-weight: bold;
        }
        .linear {
          transition: background-color 0.2s linear, border-color 0.2s linear,
                      color 0.2s linear;
        }
        .ease {
          transition: background-color 0.5s ease-in-out,
                      border-color 0.5s ease-in-out, color 0.5s ease-in-out;
        }
        .slow {
          transition: background-color 1.0s ease-out,
                      border-color 1.0s ease-out, color 1.0s ease-out;
        }
      </style>
    )html";
};

int main() {
  auto app = Ref<TransitionsDemo>::New();
  Screen screen(app);
  screen.Loop();
  return 0;
}

3. Advanced Transition Examples ​

Primary Animation & Flex Layout Demo ​

Hover over the Hover Me button and click Grow Me to observe interactive transitions reflow the flex container:

Fullscreen Demo
cpp
// Copyright 2026 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
//
// Keyframe animations.
//
// @keyframes plus the `animation` property drive a property through a cycle
// without any C++ state changing between frames.
#include <rtxui/rtxui.hpp>

using namespace rtxui;

class AnimationDemo : public Component<AnimationDemo> {
 public:
  std::string_view view = R"html(
      <div class="container">
        <h1>Animation & Transition Demo</h1>
        <p>
          Hover over the elements below to see smooth C++ terminal animations.
        </p>

        <div class="card btn-card">
          <h2>Button Hover Transitions</h2>
          <div class="btn">Hover Me</div>
        </div>

        <div class="card grow-card">
          <h2>Hover Grow Effect (Flex)</h2>
          <div class="grow-container">
            <div class="grow-box grow-box-1">Box 1</div>
            <div class="grow-box grow-box-2">Grow</div>
            <div class="grow-box grow-box-3">Box 3</div>
          </div>
        </div>
      </div>

      <style>
        self {
          --surface: rgb(22, 27, 34);
          --muted: rgb(139, 148, 158);
          --accent: rgb(88, 166, 255);
          --accent-bright: rgb(121, 192, 255);

          display: block;
          padding: 1 2;
          background-color: rgb(13, 17, 23);
          color: rgb(230, 237, 243);
        }

        .container {
          max-width: 80;
          margin: auto
        }

        h1 {
          color: var(--accent);
          margin-bottom: 1;
        }

        h2 {
          color: var(--muted);
          margin-bottom: 1;
        }

        p {
          color: var(--muted);
          margin-bottom: 2;
        }

        .card {
          display: block;
          border: tall;
          border-color: var(--surface);
          padding: 1 2;
          height: 10;
        }

        .btn {
          display: block;
          border: tall;
          border-color: var(--accent);
          background-color: rgb(30, 58, 138);
          color: rgb(191, 219, 254);
          padding: 1 3;
          text-align: center;
          width: 16;
          transition:
            background-color 0.2s ease-in-out,
            border-color 0.2s linear,
            color 0.2s ease,
            border-color 1s linear
            ;
        }
        .btn:hover {
          background-color: rgb(29, 78, 216);
          border-color: white;
          color: white;
        }
        .btn:active {
          background-color: rgb(30, 64, 175);
          border-color: var(--accent-bright);
        }

        /* 2. Hover Grow Animations */
        .grow-container {
          display: flex;
          flex-direction: row;
          width: 100%;
          height: 3;
        }
        .grow-box {
          border: tall;
          border-color: var(--surface);
          text-align: center;
          padding-top: 0;
          padding-bottom: 0;
          padding-left: 1;
          padding-right: 1;
        }
        .grow-box-1 {
          background-color: rgb(30, 58, 138);
          flex-grow: 1.0;
        }
        .grow-box-2 {
          background-color: rgb(29, 78, 216);
          flex-grow: 1.0;
          transition: flex-grow 0.4s ease-in-out, background-color 0.3s linear;
        }
        .grow-box-2:hover {
          flex-grow: 10.0;
          background-color: var(--accent-bright);
        }
        .grow-box-3 {
          background-color: rgb(30, 58, 138);
          flex-grow: 1.0;
          transition: background-color 0.3s linear;
        }
        .grow-box-3:hover {
          background-color: var(--accent-bright);
        }
      </style>
    )html";
};

int main() {
  auto app = Ref<AnimationDemo>::New();
  Screen screen(app);
  screen.Loop();
  return 0;
}

Component-Level Transition Demos ​

  • Slider Transition: Smooth track and thumb focus highlighting. (See Slider Demo)
  • Checkbox Transition: Background check state changes. (See Checkbox Demo)
  • Dropdown Select Menu: Highlight shifts. (See Select Demo)