Skip to content

Form Elements ​

RTXUI provides a set of interactive form elements for building terminal UIs with user input. Each element supports two-way data binding through the {} syntax — when the user interacts with a form control, the bound C++ variable updates automatically, and vice versa.


Input (<input>) ​

A single-line text entry field. Bind a std::string member to the value attribute for two-way updates. Supports arrow-key navigation (Ctrl to move by word), deletion (Ctrl to delete words), mouse click positioning, and horizontal scrolling on overflow.

Text selection works via Shift+arrow keys, double-click (selects a word) and click-drag, or Ctrl+A (select all). With an active selection, Ctrl+C copies and Ctrl+X cuts to the system clipboard (via the terminal's OSC 52 escape sequence, so it works over SSH without a host-side clipboard tool). Pasting uses the terminal's own paste action: RTXUI enables bracketed paste mode, so pasted text is inserted as a single update rather than one keystroke per character.

Set disabled="true" (or bind it, e.g. disabled="{is_disabled}") to remove the field from keyboard/mouse interaction and tab order entirely; it matches the :disabled CSS pseudo-class, which the default style uses to dim it. Set readonly="true" to keep the field focusable, selectable, and copyable while blocking anything that would change its value (typing, paste, backspace/delete, cut); it matches :read-only.

placeholder shows hint text (styled dim by default, via a .placeholder class) whenever value is empty, and never touches value itself.

Set type="password" to visually mask entered characters with asterisks (*) while preserving the actual plain-text secret in the bound value string.

Its selection/cursor/placeholder spans expose the same ::part() names as <textarea> (see below): part="selection" on the selected-text span, part="cursor" (also cursor-focused while focused) on the cursor cell, and part="placeholder" on the placeholder text.

maxlength caps how many graphemes value can grow to from user input (typing, pasting, Enter, Tab-indent). A paste that would overflow it is truncated to fit rather than rejected outright, matching a browser's maxlength field. It only limits interactive edits — assigning a longer string to the bound C++ variable directly is unaffected.

Ctrl+Z undoes; Ctrl+Y or Ctrl+Shift+Z redoes. Edits coalesce into a single undo step while they're contiguous — typing "abc" without moving the cursor undoes as one step, as do consecutive Backspace/Delete presses — but moving the cursor, selecting text, pasting, cutting, Enter, or Tab-indent each start a fresh step (so pasted text always undoes as a whole, separately from typing before or after it). Undo/redo is blocked while readonly or disabled.

html
<input value="{text}" />
cpp
class MyApp : public Component<MyApp> {
  std::string text = "Hello, RTXUI!";

  std::string_view view = R"html(
    <input value="{text}" />
  )html";

  MyApp() { Bind(text); }
};
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 <input> component with two-way binding.
//
// Editing the field writes straight back into the bound std::string, and the
// interpolated value below updates on the same frame.
#include <rtxui/rtxui.hpp>

using namespace rtxui;

class InputDemo : public Component<InputDemo> {
 public:
  std::string text = "Hello, RTXUI!";

  std::string_view view = R"html(
      <div class="container">
        <p class="title">Interactive Input Element</p>
        <p class="desc">Two-way binding to a std::string. Ctrl moves and deletes by word.</p>
        
        <input class="styled-input" value="{text}" />
        
        <div class="output-box">
          <span class="label">Live Value:</span>
          <span class="value">"{text}"</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);

          display: flex;
          align-items: center;
          justify-content: center;
          width: 100%;
          height: 100%;
          background-color: var(--bg);
          color: var(--text);
        }
        .container {
          display: block;
          border: tall;
          border-color: var(--border);
          background-color: rgb(22, 27, 34);
          padding: 1 3;
          width: 62;
        }
        .title {
          color: var(--accent);
          font-weight: bold;
        }
        .desc {
          color: var(--muted);
          margin-bottom: 1;
        }
        .output-box {
          display: flex;
          justify-content: space-between;
          width: 100%;
          border-top: solid;
          border-color: var(--border);
          margin-top: 1;
          padding-top: 1;
        }
        .label {
          color: var(--muted);
        }
        .value {
          color: var(--accent);
          font-weight: bold;
        }
        .slider-wrapper, .progress-wrapper {
          display: flex;
          gap: 2;
          align-items: center;
        }
        .stats {
          color: var(--muted);
          margin-top: 1;
        }
        input, textarea, select, .styled-input, .editor {
          border: tall;
          border-color: var(--border);
          background-color: var(--bg);
          color: var(--text);
          padding: 0 1;
          width: 100%;
        }
        input:focus, textarea:focus, select:focus,
        .styled-input:focus, .editor:focus {
          border-color: var(--accent);
        }
      </style>
    )html";

  InputDemo() { Bind(text); }
};

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

Textarea (<textarea>) ​

A multi-line scrollable text editor. Bind a std::string to value. Supports arrow-key navigation, Enter for newlines, Home/End for line boundaries, and Ctrl+Backspace/Delete for word deletion. Enter carries over the current line's leading indentation onto the new line; Tab/Shift-Tab indent and unindent the current line. A vertical scrollbar (scrollbar-width: auto by default, same as any other scrollable element) shows the scroll position; set scrollbar-width: none on it to hide it, as <input> does for its own horizontal scrollbar.

Selection, copy/cut, paste, disabled, readonly, placeholder, maxlength, and undo/redo all work the same way as <input> (see above; a multi-line placeholder string renders across multiple lines, same as value; maxlength also blocks Enter/Tab-indent once there's no room left; Enter and Tab-indent each undo as their own step, never merged with surrounding typing). A newline pasted into a single-line <input> is dropped; pasting into a <textarea> inserts the newlines as-is without triggering the auto-indent that a manually-typed Enter gets. Under readonly, Enter and Tab/Shift-Tab (which would otherwise insert a newline or indent) are no-ops too.

Set linenumbers to true (or absolute) to show a line-number gutter, or to relative for vim-style relative numbers (the active line shows its absolute number; every other line shows its distance from it). line_start offsets the displayed numbers (e.g. 41 to show the field as an excerpt starting at line 41 of a larger file); line_end blanks the gutter past a given displayed number (unbounded by default). line_wrap="subline" gives word-wrapped continuation rows their own blank gutter entry instead of letting the logical-line numbering drift out of alignment with them; this is a wrap-count estimate, not an exact one.

Set highlight_current_line="true" for a full-width background band behind the line the cursor is on, independently of linenumbers (works with or without a gutter).

Several internal elements expose ::part() names so an app can theme them: part="gutter" on the gutter container, part="line-number" on every row (also active on the active line's row, and wrapped on a subline continuation row), part="line" on every highlight_current_line row (also current-line on the active one), and — regardless of linenumbers/highlight_current_line — part="selection" on the selected-text span, part="cursor" (also cursor-focused while focused) on the cursor cell, and part="placeholder" on the placeholder text.

html
<textarea value="{text}" />
<textarea value="{text}" linenumbers="true" />
<textarea value="{text}" linenumbers="true" highlight_current_line="true" />
css
textarea::part(gutter)       { color: rgb(100, 116, 139); }
textarea::part(active)       { color: rgb(129, 140, 248); }
textarea::part(current-line) { background-color: rgb(49, 55, 79); }
textarea::part(selection)    { background-color: rgb(67, 56, 202); }
textarea::part(placeholder)  { color: rgb(100, 116, 139); }
cpp
class MyApp : public Component<MyApp> {
  std::string text = "Line one\nLine two\n";

  std::string_view view = R"html(
    <textarea value="{text}" />
  )html";

  MyApp() { Bind(text); }
};
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 <textarea> component: multi-line editing with a line-number gutter.
#include <rtxui/rtxui.hpp>

using namespace rtxui;

class TextareaDemo : public Component<TextareaDemo> {
 public:
  std::string text =
      "This is a multi-line\n"
      "text editor powered by RTXUI.\n"
      "\n"
      "Try editing:\n"
      "  - Arrow keys to move\n"
      "  - Enter to insert newlines\n"
      "  - Home/End to jump to line start/end\n"
      "  - Ctrl+Backspace/Delete to delete words";

  int line_count() const {
    int n = 1;
    for (char c : text) {
      if (c == '\n') {
        ++n;
      }
    }
    return n;
  }
  int char_count() const { return static_cast<int>(text.size()); }

  std::string_view view = R"html(
      <div class="container">
        <p class="title">Multi-line Textarea Editor</p>
        <p class="desc">Multi-line editing with a gutter, bound to a std::string.</p>

        <textarea class="editor" value="{text}" />

        <div class="stats">
          <span class="label">Lines:</span>
          <span class="value">{line_count}</span>
          <span class="label">Chars:</span>
          <span class="value">{char_count}</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);

          display: flex;
          align-items: center;
          justify-content: center;
          width: 100%;
          height: 100%;
          background-color: var(--bg);
          color: var(--text);
        }
        .container {
          display: block;
          border: tall;
          border-color: var(--border);
          background-color: rgb(22, 27, 34);
          padding: 1 3;
          width: 62;
        }
        .title {
          color: var(--accent);
          font-weight: bold;
        }
        .desc {
          color: var(--muted);
          margin-bottom: 1;
        }
        .output-box {
          display: flex;
          justify-content: space-between;
          width: 100%;
          border-top: solid;
          border-color: var(--border);
          margin-top: 1;
          padding-top: 1;
        }
        .label {
          color: var(--muted);
        }
        .value {
          color: var(--accent);
          font-weight: bold;
        }
        .slider-wrapper, .progress-wrapper {
          display: flex;
          gap: 2;
          align-items: center;
        }
        .stats {
          color: var(--muted);
          margin-top: 1;
        }
        input, textarea, select, .styled-input, .editor {
          border: tall;
          border-color: var(--border);
          background-color: var(--bg);
          color: var(--text);
          padding: 0 1;
          width: 100%;
        }
        input:focus, textarea:focus, select:focus,
        .styled-input:focus, .editor:focus {
          border-color: var(--accent);
        }
      </style>
    )html";

  TextareaDemo() {
    Bind(text);
    BindComputed(line_count);
    BindComputed(char_count);
  }
};

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

Checkbox (<checkbox>) ​

A boolean toggle. Bind a bool member to the checked attribute. Click or press Space when focused to toggle. The element's text content becomes the label.

The checkmark glyph exposes part="checkmark" so an app can theme it: checkbox::part(checkmark) { color: ...; }.

html
<checkbox checked="{enabled}">Enable Notifications</checkbox>
cpp
class MyApp : public Component<MyApp> {
  bool enabled = false;

  std::string_view view = R"html(
    <checkbox checked="{enabled}">Enable Notifications</checkbox>
  )html";

  MyApp() { Bind(enabled); }
};
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 <checkbox> component bound to a bool.
//
// Try it: click the box, or focus it and press Space.
#include <rtxui/rtxui.hpp>

using namespace rtxui;

class CheckboxDemo : public Component<CheckboxDemo> {
 public:
  bool checked = false;

  std::string_view view = R"html(
      <div class="container">
        <p class="title">Interactive Checkbox Element</p>
        <p class="desc">A binary toggle. Click it, or focus it and press Space.</p>
        
        <checkbox checked="{checked}">Enable Notifications</checkbox>
        
        <div class="output-box">
          <span class="label">Live Checked State:</span>
          <span class="value">{checked}</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);

          display: flex;
          align-items: center;
          justify-content: center;
          width: 100%;
          height: 100%;
          background-color: var(--bg);
          color: var(--text);
        }
        .container {
          display: block;
          border: tall;
          border-color: var(--border);
          background-color: rgb(22, 27, 34);
          padding: 1 3;
          width: 62;
        }
        .title {
          color: var(--accent);
          font-weight: bold;
        }
        .desc {
          color: var(--muted);
          margin-bottom: 1;
        }
        .output-box {
          display: flex;
          justify-content: space-between;
          width: 100%;
          border-top: solid;
          border-color: var(--border);
          margin-top: 1;
          padding-top: 1;
        }
        .label {
          color: var(--muted);
        }
        .value {
          color: var(--accent);
          font-weight: bold;
        }
        .slider-wrapper, .progress-wrapper {
          display: flex;
          gap: 2;
          align-items: center;
        }
        .stats {
          color: var(--muted);
          margin-top: 1;
        }
        input, textarea, select, .styled-input, .editor {
          border: tall;
          border-color: var(--border);
          background-color: var(--bg);
          color: var(--text);
          padding: 0 1;
          width: 100%;
        }
        input:focus, textarea:focus, select:focus,
        .styled-input:focus, .editor:focus {
          border-color: var(--accent);
        }
      </style>
    )html";

  CheckboxDemo() {
    Bind(checked);
    EnableHotReload();
  }
};

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

Radio Button (<radio>) ​

A mutually exclusive selection toggle within a named group. Radio buttons sharing the same name attribute coordinate automatically — selecting one unchecks the others in the same group. Bind a bool member to checked to observe or control the state.

html
<radio name="theme" checked="{dark_mode}">Dark Theme</radio>
<radio name="theme" checked="{light_mode}">Light Theme</radio>
cpp
class MyApp : public Component<MyApp> {
  bool dark_mode = true;
  bool light_mode = false;

  std::string_view view = R"html(
    <radio name="theme" checked="{dark_mode}">Dark Theme</radio>
    <radio name="theme" checked="{light_mode}">Light Theme</radio>
  )html";

  MyApp() {
    Bind(dark_mode);
    Bind(light_mode);
  }
};

Exposes part="radio-circle" on the radio indicator circle glyph for scoped CSS styling.

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.
//
// <radio> buttons sharing a `name`, with the selection bound to a C++ string.
#include <rtxui/rtxui.hpp>

using namespace rtxui;

class RadioDemo : public Component<RadioDemo> {
 public:
  bool select_a = false;
  bool select_b = false;
  bool select_c = false;

  std::string selected_tech() const {
    if (select_a) {
      return "PostgreSQL";
    }
    if (select_b) {
      return "MongoDB";
    }
    if (select_c) {
      return "Redis";
    }
    return "";
  }

  std::string_view view = R"html(
      <div class="content">
        <h1>RTXUI Radio Button Group Demonstration</h1>
        <p>Please select your favorite database technology:</p>
        
        <div class="group">
          <radio name="db" checked="{select_a}">PostgreSQL (Robust, relational)</radio>
          <radio name="db" checked="{select_b}">MongoDB (Document-based)</radio>
          <radio name="db" checked="{select_c}">Redis (In-memory cache)</radio>
        </div>

        <p class="status">
          Selected Tech: {selected_tech}
        </p>
      </div>

      <style>
        self {

          display: block;
          padding: 1;
          background-color: rgb(13, 17, 23);
        }
        h1 {
          color: rgb(88, 166, 255);
          margin-bottom: 1;
        }
        p {
          margin-bottom: 1;
        }
        .group {
          display: flex;
          flex-direction: column;
          gap: 1;
          margin-bottom: 1;
          border-left: solid;
          border-color: rgb(48, 54, 61);
          padding-left: 2;
        }
        .status {
          font-weight: bold;
          color: rgb(234, 179, 8);
        }
      </style>
    )html";

  RadioDemo() {
    Bind(select_a);
    Bind(select_b);
    Bind(select_c);
    Bind(selected_tech);
  }
};

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

Slider (<slider>) ​

A range slider control. Bind an int to value and configure the range with min, max, step, and width attributes. Drag the thumb with the mouse or use Arrow Keys when focused.

html
<slider value="{volume}" min="0" max="100" step="5" width="30" />
cpp
class MyApp : public Component<MyApp> {
  int volume = 50;

  std::string_view view = R"html(
    <slider value="{volume}" min="0" max="100" step="5" width="30" />
    <span>{volume}%</span>
  )html";

  MyApp() { Bind(volume); }
};
AttributeTypeDescription
valueintCurrent value (two-way bound)
minintMinimum value (default 0)
maxintMaximum value (default 100)
stepintIncrement per step (default 1)
widthintDisplay width in terminal columns

Exposes part="slider-container" on the outer track wrapper, and part="track-left"/part="thumb"/part="track-right" on the three segments either side of and including the thumb.

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 <slider> component bound to an int.
//
// Try it: drag the thumb, or focus it and use the arrow keys.
#include <rtxui/rtxui.hpp>

using namespace rtxui;

class SliderDemo : public Component<SliderDemo> {
 public:
  int volume = 50;

  std::string_view view = R"html(
      <div class="container">
        <p class="title">Interactive Slider Element</p>
        <p class="desc">Drag the thumb, or focus it and use the arrow keys.</p>
        
        <div class="slider-wrapper">
          <span class="label">Volume:</span>
          <slider value="{volume}" min="0" max="100" step="5" width="30" />
        </div>
        
        <div class="output-box">
          <span class="label">Live Volume Value:</span>
          <span class="value">{volume}%</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);

          display: flex;
          align-items: center;
          justify-content: center;
          width: 100%;
          height: 100%;
          background-color: var(--bg);
          color: var(--text);
        }
        .container {
          display: block;
          border: tall;
          border-color: var(--border);
          background-color: rgb(22, 27, 34);
          padding: 1 3;
          width: 62;
        }
        .title {
          color: var(--accent);
          font-weight: bold;
        }
        .desc {
          color: var(--muted);
          margin-bottom: 1;
        }
        .output-box {
          display: flex;
          justify-content: space-between;
          width: 100%;
          border-top: solid;
          border-color: var(--border);
          margin-top: 1;
          padding-top: 1;
        }
        .label {
          color: var(--muted);
        }
        .value {
          color: var(--accent);
          font-weight: bold;
        }
        .slider-wrapper, .progress-wrapper {
          display: flex;
          gap: 2;
          align-items: center;
        }
        .stats {
          color: var(--muted);
          margin-top: 1;
        }
        input, textarea, select, .styled-input, .editor {
          border: tall;
          border-color: var(--border);
          background-color: var(--bg);
          color: var(--text);
          padding: 0 1;
          width: 100%;
        }
        input:focus, textarea:focus, select:focus,
        .styled-input:focus, .editor:focus {
          border-color: var(--accent);
        }
      </style>
    )html";

  SliderDemo() { Bind(volume); }
};

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

Progress Bar (<progress>) ​

A non-interactive progress indicator. Bind an int to value and set the max attribute to define the range. Use width to control the bar length.

html
<progress value="{progress_val}" max="100" width="30" />
cpp
class MyApp : public Component<MyApp> {
  int progress_val = 45;

  std::string_view view = R"html(
    <progress value="{progress_val}" max="100" width="30" />
    <span>{progress_val}%</span>
  )html";

  MyApp() { Bind(progress_val); }
};
AttributeTypeDescription
valueintCurrent progress value (bound)
maxintMaximum value (default 100)
widthintDisplay width in terminal columns

Exposes part="filled" and part="empty" on the two track segments, e.g. progress::part(filled) { color: ...; }.

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 <progress> component driven by bound state.
#include <rtxui/rtxui.hpp>

using namespace rtxui;

class ProgressDemo : public Component<ProgressDemo> {
 public:
  int progress_val = 45;

  std::string_view view = R"html(
      <div class="container">
        <p class="title">Interactive Progress Bar Element</p>
        <p class="desc">A determinate progress bar driven by bound state.</p>
        
        <div class="slider-wrapper">
          <span class="label">Control:</span>
          <slider value="{progress_val}" min="0" max="100" step="1" width="30" />
        </div>

        <div class="progress-wrapper">
          <span class="label">Progress:</span>
          <progress value="{progress_val}" max="100" width="30" />
        </div>
        
        <div class="output-box">
          <span class="label">Current Completion:</span>
          <span class="value">{progress_val}%</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);

          display: flex;
          align-items: center;
          justify-content: center;
          width: 100%;
          height: 100%;
          background-color: var(--bg);
          color: var(--text);
        }
        .container {
          display: block;
          border: tall;
          border-color: var(--border);
          background-color: rgb(22, 27, 34);
          padding: 1 3;
          width: 62;
        }
        .title {
          color: var(--accent);
          font-weight: bold;
        }
        .desc {
          color: var(--muted);
          margin-bottom: 1;
        }
        .output-box {
          display: flex;
          justify-content: space-between;
          width: 100%;
          border-top: solid;
          border-color: var(--border);
          margin-top: 1;
          padding-top: 1;
        }
        .label {
          color: var(--muted);
        }
        .value {
          color: var(--accent);
          font-weight: bold;
        }
        .slider-wrapper, .progress-wrapper {
          display: flex;
          gap: 2;
          align-items: center;
        }
        .stats {
          color: var(--muted);
          margin-top: 1;
        }
        input, textarea, select, .styled-input, .editor {
          border: tall;
          border-color: var(--border);
          background-color: var(--bg);
          color: var(--text);
          padding: 0 1;
          width: 100%;
        }
        input:focus, textarea:focus, select:focus,
        .styled-input:focus, .editor:focus {
          border-color: var(--accent);
        }
      </style>
    )html";

  ProgressDemo() { Bind(progress_val); }
};

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

Select / Dropdown (<select>) ​

A dropdown picker menu. Bind a std::string to value. Children are <option> elements whose value attributes determine the bound string. Click to open, or focus with Tab and use Enter/Space. Navigate options with ArrowUp/ArrowDown and confirm with Enter.

html
<select value="{my_theme}">
  <option value="dark">Dark Theme</option>
  <option value="light">Light Theme</option>
  <option value="solarized">Solarized</option>
</select>
cpp
class MyApp : public Component<MyApp> {
  std::string my_theme = "light";

  std::string_view view = R"html(
    <select value="{my_theme}">
      <option value="dark">Dark Theme</option>
      <option value="light">Light Theme</option>
      <option value="solarized">Solarized</option>
    </select>
  )html";

  MyApp() { Bind(my_theme); }
};

Exposes part="select-btn" on the closed button, part="select-label"/part="select-arrow" on its two children, and part="dropdown-list" on the open dropdown panel.

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 <select> dropdown with <option> children.
//
// Try it: focus it and press Enter or Space to open, then arrows to choose.
#include <rtxui/rtxui.hpp>

using namespace rtxui;

class SelectDemo : public Component<SelectDemo> {
 public:
  std::string my_theme = "light";

  std::string_view view = R"html(
      <div class="container">
        <p class="title">Interactive Select & Option Elements</p>
        <p class="desc">Focus it and press Enter or Space to open, arrows to choose.</p>
        
        <select value="{my_theme}">
          <option value="dark">Dark Theme</option>
          <option value="light">Light Theme</option>
          <option value="solarized">Solarized</option>
        </select>
        
        <div class="output-box">
          <span class="label">Current Theme Value:</span>
          <span class="value">{my_theme}</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);

          display: flex;
          align-items: center;
          justify-content: center;
          width: 100%;
          height: 100%;
          background-color: var(--bg);
          color: var(--text);
        }
        .container {
          display: block;
          border: tall;
          border-color: var(--border);
          background-color: rgb(22, 27, 34);
          padding: 1 3;
          width: 62;
        }
        .title {
          color: var(--accent);
          font-weight: bold;
        }
        .desc {
          color: var(--muted);
          margin-bottom: 1;
        }
        .output-box {
          display: flex;
          justify-content: space-between;
          width: 100%;
          border-top: solid;
          border-color: var(--border);
          margin-top: 1;
          padding-top: 1;
        }
        .label {
          color: var(--muted);
        }
        .value {
          color: var(--accent);
          font-weight: bold;
        }
        .slider-wrapper, .progress-wrapper {
          display: flex;
          gap: 2;
          align-items: center;
        }
        .stats {
          color: var(--muted);
          margin-top: 1;
        }
        input, textarea, select, .styled-input, .editor {
          border: tall;
          border-color: var(--border);
          background-color: var(--bg);
          color: var(--text);
          padding: 0 1;
          width: 100%;
        }
        input:focus, textarea:focus, select:focus,
        .styled-input:focus, .editor:focus {
          border-color: var(--accent);
        }
      </style>
    )html";

  SelectDemo() {
    Bind(my_theme);
    EnableHotReload();
  }
};

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

Label (<label>) ​

A helper element that delegates mouse click events and focus to an associated interactive element (like a checkbox or text input). It can associate either explicitly via the for attribute (matching the target element's id), or implicitly by nesting the target element inside the <label>.

Explicit Association (via for attribute) ​

html
<label for="my-checkbox">Notification Settings</label>
<checkbox id="my-checkbox" checked="{enabled}">Enable Alerts</checkbox>

Implicit Association (via Nesting) ​

html
<label>
  Accept Terms
  <checkbox checked="{accepted}">Accept</checkbox>
</label>
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.
//
// <label> delegating clicks and focus to the control it names.
#include <rtxui/rtxui.hpp>

using namespace rtxui;

class LabelDemo : public Component<LabelDemo> {
 public:
  bool cb1_checked = false;
  bool cb2_checked = false;

  std::string_view view = R"html(
      <div class="container">
        <p class="title">Interactive Label Element</p>
        <p class="desc">
          Clicking on a label delegates focus and actions to its associated input/checkbox.
        </p>

        <!-- Explicit Association using "for" -->
        <div class="section">
          <p class="section-title">1. Explicit Association (via "for" attribute)</p>
          <div class="row">
            <label class="btn-label" for="explicit-cb">Click Me (Explicit Label)</label>
            <checkbox id="explicit-cb" checked="{cb1_checked}">Checkbox 1</checkbox>
          </div>
          <p class="state-label">Checked state: <span class="value">{cb1_checked}</span></p>
        </div>

        <!-- Implicit Association using Nesting -->
        <div class="section">
          <p class="section-title">2. Implicit Association (via Nesting)</p>
          <label class="nest-label">
            Click Anywhere Here (Implicit Label)
            <checkbox checked="{cb2_checked}">Checkbox 2</checkbox>
          </label>
          <p class="state-label">Checked state: <span class="value">{cb2_checked}</span></p>
        </div>
      </div>

      <style>
        self {
          --surface: rgb(22, 27, 34);
          --muted: rgb(139, 148, 158);

          display: block;
          padding: 1;
          background-color: rgb(13, 17, 23);
          color: white;
        }
        .container {
          display: block;
        }
        .title {
          display: block;
          font-weight: bold;
          color: rgb(56, 189, 248);
          margin-bottom: 1;
        }
        .desc {
          display: block;
          color: var(--muted);
          margin-bottom: 2;
        }
        .section {
          display: block;
          margin-bottom: 2;
          border: round;
          border-color: rgb(75, 85, 99);
          padding: 1;
        }
        .section-title {
          display: block;
          font-weight: bold;
          color: rgb(234, 179, 8);
          margin-bottom: 1;
        }
        .row {
          display: flex;
          align-items: center;
          gap: 4;
        }
        .btn-label {
          background-color: var(--surface);
          padding-left: 1;
          padding-right: 1;
          border: solid;
          border-color: rgb(88, 166, 255);
        }
        .nest-label {
          display: flex;
          align-items: center;
          gap: 2;
          background-color: var(--surface);
          padding: 1;
          border: dashed;
          border-color: rgb(63, 185, 80);
        }
        .state-label {
          display: block;
          margin-top: 1;
          color: var(--muted);
        }
        .value {
          font-weight: bold;
          color: rgb(56, 189, 248);
        }
      </style>
    )html";

  LabelDemo() {
    Bind(cb1_checked);
    Bind(cb2_checked);
    EnableHotReload();
  }
};

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