Skip to content

HTML Element & Component Reference ​

This specification defines the complete set of built-in XML/HTML tags and components supported by the RTXUI template parser, including their classification, attribute interfaces, slot projections, event contracts, and default user-agent stylesheets.


1. Architectural Model ​

1.1 Unified Component Architecture ​

Every built-in tag—from <div> to <dialog>—is a component registered via RegisterGlobalComponent() and built over rtxui::Element. Simpler tags like <div> or <span> use trivial templates (e.g. <slot></slot> with a default stylesheet), while richer widgets like <input> or <select> encapsulate internal HTML templates, reactive state bindings, slot projections, and scoped CSS rules. The difference is in complexity, not in kind.

1.2 Parser Contract ​

  • Well-Formed XML/HTML: All tags must be balanced or explicitly self-closed (e.g. <hr />, <br />, <input />). Unclosed tags generate parser errors with line and column diagnostics.
  • Case Sensitivity: Tag names and attribute names are parsed as lower-case identifiers.
  • Quoting: Attribute values must be enclosed in double ("...") or single ('...') quotes.

1.3 Binding Syntax ​

SyntaxTargetExampleSemantic Contract
{member}Text / Attribute<span id="{node_id}">{title}</span>Interpolates C++ member snapshot; updates DOM on change.
:attr="member"Attribute<input :value="username" />Equivalent to attr="{member}".
on<event>="Handler"Event Callback<button onclick="Submit">OK</button>Dispatches to registered C++ method or lambda.
@<event>="Handler"Event Callback<button @click="Submit">OK</button>Shorthand for event dispatch.
@<event>.<mod>="Handler"Event Modifier<button @click.right="OnMenu">OK</button>Restricts dispatch to specific event conditions (e.g. mouse right-click).

2. Global Attributes & Directives ​

The following attributes apply to all elements and components:

AttributeTypeDefaultDescription
idstring""Unique element identifier; targets #id CSS rules and for associations.
classstring""Space-delimited token list for .class CSS selector matching.
stylestring""Inline CSS declarations. Overrides stylesheet rules with higher specificity.
tabindexinteger-1Sequential keyboard focus order. Values >= 0 participate in Tab navigation; -1 excludes the element.
focusablebooleanfalseShorthand; focusable="true" is equivalent to tabindex="0".
disabledbooleanfalseDisables user interaction, removes focus, and applies the :disabled CSS pseudo-class.
ifexpression—Conditional rendering directive. If the expression evaluates to false, the element and its subtree are pruned from the DOM.
partstring""Names an element part for ::part() CSS selector targeting from ancestor components.

3. Structural Containers & Layout ​

<div> ​

  • Classification: Block Component
  • Layout Model: display: block
  • Semantics: Standard block-level container for flow, flex, or grid layouts.

<span> ​

  • Classification: Inline Component
  • Layout Model: display: inline
  • Semantics: Inline-level formatting wrapper for styled text segments.

Semantic Elements (<header>, <footer>, <main>, <nav>, <aside>, <section>, <article>) ​

  • Classification: Block Components
  • Layout Model: display: block
  • Semantics: Structural containers functionally identical to <div>, providing semantic document organization.

<table>, <thead>, <tbody>, <tfoot>, <tr>, <th>, <td> ​

  • Classification: Table Components
  • Layout Model: Table formatting context with automatic column and row sizing.
  • Section Containers: <thead>, <tbody>, and <tfoot> group rows logically. The layout engine traverses them transparently when assembling grid rows and computing cell geometry.

<th> / <td> Attributes ​

AttributeTypeDefaultReactiveDescription
colspaninteger1One-wayNumber of columns the cell should span.
rowspaninteger1One-wayNumber of rows the cell should span.

Table Sizing Algorithm: ​

  • Columns: Evaluates the maximum preferred cell width across each column. Extra horizontal space from fixed container width or min-width is distributed proportionally.
  • Rows: Row height conforms to the tallest cell in the row. Shorter cells stretch vertically to maintain unbroken borders and backgrounds.
xml
<table>
  <thead>
    <tr>
      <th>Service</th>
      <th>Status</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Database</td>
      <td>Active</td>
    </tr>
  </tbody>
</table>

<style>
  table {
    border: solid;
    border-color: #3b82f6;
  }
  th {
    font-weight: bold;
    border-bottom: solid;
    border-color: #334155;
  }
  td, th {
    padding-left: 1;
    padding-right: 1;
  }
</style>
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.
//
// Table elements: <table>, <thead>, <tr>, <th>, <td>, with colspan, rowspan and
// a sticky header row.
#include <rtxui/rtxui.hpp>

using namespace rtxui;

class TableDemo : public Component<TableDemo> {
 public:
  std::string_view view = R"html(
      <div class="content">
        <h1>RTXUI Table Layout Demo</h1>
        <p>This demo showcases standard table elements (&lt;table&gt;, &lt;tr&gt;, &lt;th&gt;, &lt;td&gt;) with dynamic layout and styling. Scroll down to see the sticky header!</p>

        <table>
          <thead>
            <tr class="header-row">
              <th>ID</th>
              <th>Name</th>
              <th>Category</th>
              <th>Price</th>
              <th>Status</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>#101</td>
              <td>Gadget A</td>
              <td>Appliances</td>
              <td>$10.00</td>
              <td class="status active">Active</td>
            </tr>
            <tr class="alt-row">
              <td>#117</td>
              <td>Widget Z</td>
              <td>Consumer Electronics</td>
              <td>$15.00</td>
              <td class="status active">Active</td>
            </tr>

            <tr>
              <td>#102</td>
              <td>Gadget B</td>
              <td>Appliances</td>
              <td>$49.50</td>
              <td class="status active">Active</td>
            </tr>
            <tr class="alt-row">
              <td>#103</td>
              <td>Tool C</td>
              <td>Hardware</td>
              <td>$5.99</td>
              <td class="status disabled">Inactive</td>
            </tr>
            <tr>
              <td>#104</td>
              <td>Widget D</td>
              <td style="white-space: nowrap; text-overflow: ellipsis; overflow: hidden; max-width: 15;">Consumer Electronics</td>
              <td>$129.00</td>
              <td class="status active">Active</td>
            </tr>
            <tr class="alt-row">
              <td>#105</td>
              <td>Smartwatch E</td>
              <td>Wearables</td>
              <td>$199.99</td>
              <td class="status active">Active</td>
            </tr>
            <tr>
              <td colspan="2" class="border">colspan=2</td>
              <td>Computers</td>
              <td>$899.00</td>
              <td class="status active">Active</td>
            </tr>
            <tr class="alt-row">
              <td>#107</td>
              <td>Keyboard G</td>
              <td>Accessories</td>
              <td>$45.00</td>
              <td class="status disabled">Inactive</td>
            </tr>
            <tr>
              <td rowspan="4" class="border">rowspan=4</td>
              <td>Mouse H</td>
              <td>Accessories</td>
              <td>$25.50</td>
              <td class="status active">Active</td>
            </tr>
            <tr class="alt-row">
              <td>Monitor I</td>
              <td>Computers</td>
              <td>$249.99</td>
              <td class="status active">Active</td>
            </tr>
            <tr>
              <td>Desk Lamp J</td>
              <td>Furniture</td>
              <td>$35.00</td>
              <td class="status disabled">Inactive</td>
            </tr>
            <tr class="alt-row">
              <td>Chair K</td>
              <td>Furniture</td>
              <td>$150.00</td>
              <td class="status active">Active</td>
            </tr>
            <tr>
              <td>#112</td>
              <td>Headphones L</td>
              <td>Audio</td>
              <td>$79.99</td>
              <td class="status active">Active</td>
            </tr>
            <tr class="alt-row">
              <td>#113</td>
              <td>Speaker M</td>
              <td>Audio</td>
              <td>$120.00</td>
              <td class="status disabled">Inactive</td>
            </tr>
            <tr>
              <td>#114</td>
              <td>Cable N</td>
              <td>Accessories</td>
              <td>$9.99</td>
              <td class="status active">Active</td>
            </tr>
            <tr class="alt-row">
              <td>#115</td>
              <td>Adapter O</td>
              <td>Accessories</td>
              <td>$15.00</td>
              <td class="status active">Active</td>
            </tr>
            <tr>
              <td>#116</td>
              <td>Camera P</td>
              <td>Photography</td>
              <td>$450.00</td>
              <td class="status active">Active</td>
            </tr>
            <tr class="alt-row">
              <td>#117</td>
              <td>Lens Q</td>
              <td>Photography</td>
              <td>$300.00</td>
              <td class="status disabled">Inactive</td>
            </tr>
            <tr>
              <td>#118</td>
              <td>Tripod R</td>
              <td>Photography</td>
              <td>$60.00</td>
              <td class="status active">Active</td>
            </tr>
            <tr class="alt-row">
              <td>#119</td>
              <td>Bag S</td>
              <td>Accessories</td>
              <td>$40.00</td>
              <td class="status active">Active</td>
            </tr>
            <tr>
              <td>#120</td>
              <td>Phone T</td>
              <td style="white-space: nowrap; text-overflow: ellipsis; overflow: hidden; max-width: 15;">Consumer Electronics</td>
              <td>$699.99</td>
              <td class="status active">Active</td>
            </tr>
          </tbody>
        </table>
      </div>

      <style>
        self {
          --border: rgb(48, 54, 61);

          display: block;
          padding: 1;
          background-color: rgb(13, 17, 23);
          color: white;
          width: 100%;
          height: 100%;
          overflow-y: scroll;
        }
        .content {
          display: block;
          max-width: 80;
          margin: 0 auto;
        }
        h1 {
          font-weight: bold;
          margin-bottom: 1;
          color: rgb(88, 166, 255);
        }
        p {
          margin-bottom: 2;
          color: rgb(139, 148, 158);
        }
        table {
          display: block;
          border: solid;
          border-color: var(--border);
          width: 100%;
        }
        tr {
          display: block;
          background-color: rgb(15, 23, 42); 
        }
        th {
          font-weight: bold;
          color: rgb(121, 192, 255);
          border-bottom: solid;
          border-color: var(--border);
          padding: 1;
        }
        td {
          padding: 1;
        }
        .status {
          font-weight: bold;
        }
        .status.active {
          color: rgb(74, 222, 128);
        }
        .status.disabled {
          color: rgb(248, 81, 73);
        }
        .border {
          border: tall;
          border-color: rgb(74, 222, 128);
          background-color: rgb(15, 23, 42);
          padding: 0;
        }
        tr.alt-row {
          background-color: rgb(22, 27, 34); 
        }
        .header-row {
          position: sticky;
          top: 0;
          z-index: 10;
          background-color: rgb(15, 23, 42);
        }
      </style>
    )html";
};

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

4. Form Controls & Interactive Widgets ​

<button> ​

  • Classification: Inline Component (rtxui::button)
  • Focusable: Yes (tabindex="0")

Attributes ​

AttributeTypeDefaultReactiveDescription
disabledbooleanfalseOne-waySuppresses activation events and sets :disabled pseudo-class.
onclickhandler——Callback invoked on mouse click or Enter/Space keyboard activation.

Slots ​

SlotTarget ElementDescription
(default)Inline containerButton text or child element content.

Default Stylesheet ​

css
self {
  display: inline-block;
  padding-left: 1;
  padding-right: 1;
  cursor: pointer;
  background-color: lighten(10%);
  opacity: 0.85;
  transition: background-color 0.1s linear, opacity 0.1s linear, color 0.1s linear;
}
self:hover    { background-color: rgb(59, 130, 246); color: white; opacity: 0.95; }
self:focus    { background-color: rgb(37, 99, 235); color: white; opacity: 1.0; }
self:active   { background-color: rgb(29, 78, 216); color: white; opacity: 1.0; }
self:disabled { cursor: default; opacity: 0.4; }

<input> ​

  • Classification: Inline Component (rtxui::input)
  • Focusable: Yes (tabindex="0")

Attributes ​

AttributeTypeDefaultReactiveDescription
valuestring""Two-wayText content. Automatically synchronized during typing.
typestring"text"One-wayInput format. "text" renders characters; "password" masks characters with *.
placeholderstring""One-wayGhost text displayed when value is empty.
disabledbooleanfalseOne-wayDisables input and focus; applies :disabled pseudo-class.
readonlybooleanfalseOne-wayPrevents text modification while allowing focus and text selection.
select_on_focusbooleanfalseOne-wayAutomatically selects all text content when focused.
maxlengthinteger-1One-wayMaximum character length constraint (-1 indicates unconstrained).
onchangehandler——Callback invoked when value is committed. Accepts void() or void(std::string).

Styleable Parts (::part()) ​

  • placeholder: The span element rendering placeholder text.
  • cursor, cursor-focused: The active cursor indicator.
  • selection: Highlighted text selection regions.

Default Stylesheet ​

css
self {
  display: inline-flex;
  flex-direction: row;
  width: 20;
  padding-left: 1;
  padding-right: 1;
  overflow-x: scroll;
  scrollbar-width: none;
  white-space: nowrap;
  background-color: rgb(40, 40, 40);
  opacity: 0.8;
}
self:focus {
  opacity: 1.0;
}
self:disabled {
  opacity: 0.4;
  cursor: default;
}

<textarea> ​

  • Classification: Block Component (rtxui::textarea)
  • Focusable: Yes (tabindex="0")

Attributes ​

AttributeTypeDefaultReactiveDescription
valuestring""Two-wayMulti-line text content.
placeholderstring""One-wayGhost text rendered when content is empty.
disabledbooleanfalseOne-wayDisables editing and interaction.
readonlybooleanfalseOne-wayPrevents text edits while maintaining scroll and selection capabilities.
select_on_focusbooleanfalseOne-wayAutomatically selects all text content when focused.
maxlengthinteger-1One-wayMaximum character capacity limit.
linenumbersstring""One-wayDisplays line number gutter. Values: "" (off), "true" / "absolute" (absolute), "relative" (relative).
line_startinteger1One-wayThe displayed number of the first logical line.
line_endinteger-1One-wayThe last displayed number to show a gutter entry for; -1 means unbounded.
line_wrapstring""One-waySoft line wrapping at viewport boundary. Values: "", "ignore", "subline".
highlight_current_linebooleanfalseOne-wayEmphasizes the row hosting the active cursor.

<checkbox> ​

  • Classification: Inline-Flex Component (rtxui::checkbox)
  • Focusable: Yes (tabindex="0")

Attributes ​

AttributeTypeDefaultReactiveDescription
checkedbooleanfalseTwo-wayState toggle. Toggles between ☑ and ☐. Synchronizes :checked pseudo-class.
disabledbooleanfalseOne-waySuppresses toggle interaction and sets :disabled pseudo-class.
onchangehandler——Fired on state change. Accepts void() or void(bool).

Styleable Parts (::part()) ​

  • checkmark: Span element containing the checkmark glyph (☑ or ☐).

Default Stylesheet ​

css
self {
  display: inline-flex;
  flex-direction: row;
  white-space: nowrap;
  cursor: pointer;
  padding-left: 1;
  padding-right: 1;
  transition: background-color 0.1s linear;
}
self:hover    { background-color: lighten(10%); }
self:focus    { background-color: lighten(25%); }
self:active   { background-color: lighten(35%); }
self:disabled { cursor: default; opacity: 0.4; }
.checkmark    { margin-right: 1; }

<radio> ​

  • Classification: Inline-Flex Component (rtxui::radio)
  • Focusable: Yes (tabindex="0")

Attributes ​

AttributeTypeDefaultReactiveDescription
namestring""One-wayRadio group identifier. Only one radio sharing a name can be checked simultaneously.
checkedbooleanfalseTwo-waySelection state. Toggles between ◉ and ○. Synchronizes :checked pseudo-class.
disabledbooleanfalseOne-waySuppresses activation and applies :disabled pseudo-class.
onchangehandler——Fired when the radio is selected.

Styleable Parts (::part()) ​

  • radio-mark: Span element containing the radio glyph (◉ or ○).
xml
<radio name="theme" checked="{dark_mode}">Dark</radio>
<radio name="theme" checked="{light_mode}">Light</radio>
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;
}

<select> and <option> ​

  • Classification: Component (rtxui::select, rtxui::option)
  • Focusable: Yes (tabindex="0")

<select> Attributes ​

AttributeTypeDefaultReactiveDescription
valuestring""Two-wayActive selected option value.
disabledbooleanfalseOne-wayDisables dropdown expansion.
onchangehandler——Fired on option change. Accepts void() or void(std::string).

<option> Attributes ​

AttributeTypeDefaultReactiveDescription
valuestring""One-wayDistinct value payload passed to <select>.
disabledbooleanfalseOne-wayPrevents selection of this specific option.

Styleable Parts (::part()) ​

  • select-btn: Main clickable trigger container.
  • select-label: Active selected text label.
  • select-arrow: Dropdown expand/collapse arrow glyph.
  • dropdown-list: Floating options list container.

<slider> ​

  • Classification: Inline Component (rtxui::slider)
  • Focusable: Yes (tabindex="0")

Attributes ​

AttributeTypeDefaultReactiveDescription
valueinteger0Two-wayCurrent slider value. Clamped between min and max.
mininteger0One-wayLower bound of the range.
maxinteger100One-wayUpper bound of the range.
stepinteger1One-wayDiscrete increment step on keyboard navigation or dragging.
widthinteger20One-wayVisual cell track width.
directionstring"horizontal"One-wayTrack orientation ("horizontal" or "vertical").
disabledbooleanfalseOne-wayDisables slider manipulation.
onchangehandler——Fired on value updates. Accepts void() or void(int).

Styleable Parts (::part()) ​

  • slider-container: Wrapper element.
  • track-left: Filled track portion preceding the thumb.
  • thumb: Visual thumb handle glyph (●).
  • track-right: Unfilled track portion following the thumb.

<progress> ​

  • Classification: Inline Component (rtxui::progress)
  • Focusable: No

Attributes ​

AttributeTypeDefaultReactiveDescription
valuefloat0.0One-wayCurrent progress amount.
maxfloat100.0One-wayMaximum range value.
widthinteger20One-wayPhysical cell width of the bar.

Styleable Parts (::part()) ​

  • filled: Active progress section (rendered using █).
  • empty: Inactive remainder section.

<label> ​

  • Classification: Inline-Flex Component (rtxui::label)
  • Focusable: No (cursor: pointer)

Attributes ​

AttributeTypeDefaultReactiveDescription
forstring""One-wayID of target interactive element.

Event Delegation Semantics: ​

Clicking a <label> delegates focus and activation to the target:

  1. Explicit Target: Element whose id matches the for attribute.
  2. Implicit Target: The first focusable descendant nested within <label></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;
}

5. Disclosure, Navigation & Overlays ​

<details> and <summary> ​

  • Classification: Block Component (rtxui::details, rtxui::summary)

Attributes ​

AttributeTypeDefaultReactiveDescription
openbooleanfalseTwo-wayExpansion state. Toggled by clicking <summary> or pressing Enter/Space.

Slots ​

SlotTarget ElementDescription
summary<summary>Clickable disclosure header row. If omitted, defaults to "Details".
(default)Block containerCollapsible body content displayed when open="true".
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 <details>/<summary> disclosure widget.
//
// Try it: click a summary row, or focus it and press Enter.
#include <memory>

#include "rtxui/internal/component.hpp"
#include "rtxui/internal/screen.hpp"

namespace {

class DetailsDemo : public rtxui::Component<DetailsDemo> {
 public:
  bool open1 = false;
  bool open2 = true;
  bool checkbox_state = false;

  std::string status_text() const {
    return checkbox_state ? "DEBUG ENABLED" : "DEBUG DISABLED";
  }

  DetailsDemo() {
    Bind(open1);
    Bind(open2);
    Bind(checkbox_state);
    Bind(status_text);
  }

  std::string_view Setup() {
    return R"html(
      <div class="app">
        <h1>RTXUI Details &amp; Summary Demonstration</h1>

        <div class="row">
          <div class="col">
            <details open="{open1}">
              <summary>System Logs</summary>
              <p>[09:41:02] Application initialized.</p>
              <p>[09:41:05] Theme loaded: light.</p>
              <p>[09:42:15] User input detected.</p>
            </details>
          </div>

          <div class="col">
            <details open="{open2}">
              <summary>Preferences &amp; Settings</summary>
              <div class="settings-box">
                <checkbox checked="{checkbox_state}">Enable debug mode</checkbox>
                <p>Status: {status_text}</p>
              </div>
            </details>
          </div>
        </div>

        <div class="row info-row">
          <details>
            <p>This is a collapsible widget using the default summary header.</p>
            <p>Since no &lt;summary&gt; tag was defined, it automatically defaulted to "Details".</p>
          </details>
        </div>
      </div>

      <style>
        self {

          background-color: rgb(13, 17, 23);
          display: block;
          padding: 1;
        }
        .app {
          display: flex;
          flex-direction: column;
          gap: 1;
          border: solid;
          border-color: #3b82f6;
          padding: 1;
        }
        h1 {
          color: #3b82f6;
          margin-bottom: 1;
        }
        .row {
          display: flex;
          flex-direction: row;
          gap: 2;
        }
        .col {
          flex: 1;
          border: round;
          border-color: #555;
          padding: 1;
        }
        .settings-box {
          display: flex;
          flex-direction: column;
          gap: 1;
          padding-left: 1;
          border-left: solid;
          border-color: #f59e0b;
        }
        .info-row {
          margin-top: 1;
          padding: 1;
          background-color: lighten(5%);
          border: dashed;
          border-color: #888;
        }
      </style>
    )html";
  }
};

}  // namespace

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

<fieldset> and <legend> ​

  • Classification: Block Component (rtxui::fieldset, rtxui::legend)

Attributes ​

AttributeTypeDefaultReactiveDescription
disabledbooleanfalseOne-wayDisables the fieldset and all enclosed form controls.

Slots ​

SlotTarget ElementDescription
legend<legend>Header caption nested into the top border frame.
(default)Block containerEnclosed form controls and content.
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.
//
// <fieldset> and <legend> grouping.
//
// The legend is nested into the top border of the group it captions.
#include <rtxui/rtxui.hpp>

using namespace rtxui;

class FieldsetDemo : public Component<FieldsetDemo> {
 public:
  std::string_view view = R"html(
      <div class="content">
        <fieldset>
          <legend>Personal Identification</legend>
          <p>Name: Arthur Sonzogni</p>
          <p>Role: Engineer & Creator</p>
        </fieldset>

        <fieldset>
          <legend>Project Metadata</legend>
          <p>Repository: RTXUI (Reactive Terminal User Interface)</p>
          <p>Language: C++23</p>
        </fieldset>
      </div>

      <style>
        self {

          display: block;
          padding: 1;
          background-color: rgb(13, 17, 23);
        }
        .content {
          display: flex;
          flex-direction: column;
          gap: 1;
        }
        p {
          margin-top: 1;
        }
      </style>
    )html";
};

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

<tabs> and <tab-pane> ​

  • Classification: Component (rtxui::tabs, rtxui::tab_pane)

<tabs> Attributes ​

AttributeTypeDefaultReactiveDescription
valuestring""Two-wayActive pane identifier matching <tab-pane name="...">.
onchangehandler——Fired when active tab changes.

<tab-pane> Attributes ​

AttributeTypeDefaultReactiveDescription
namestring""One-wayUnique tab identifier.
labelstring""One-wayHeader text displayed on the tab button.
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 built-in <tabs>/<tab-pane> components.
//
// The active pane is selected by the `value` attribute bound to a C++ string.
#include <rtxui/rtxui.hpp>

using namespace rtxui;

class TabsDemo : public Component<TabsDemo> {
 public:
  std::string current_tab = "home";

  std::string_view view = R"html(
      <div class="content">
        <h1>RTXUI Tabbed Navigation Interface</h1>

        <tabs value="{current_tab}">
          <tab-pane label="Dashboard" name="home">
            <p class="tab-body">Welcome to your Dashboard. Active processes: 14</p>
          </tab-pane>
          <tab-pane label="Settings" name="settings">
            <p class="tab-body">System preferences and visual theme modifications.</p>
          </tab-pane>
          <tab-pane label="About" name="about">
            <p class="tab-body">RTXUI Version 0.1.0 - Created by Arthur Sonzogni.</p>
          </tab-pane>
        </tabs>
      </div>

      <style>
        self {

          display: block;
          padding: 1;
          background-color: rgb(13, 17, 23);
        }
        h1 {
          color: rgb(88, 166, 255);
          margin-bottom: 2;
        }
        .tab-body {
          border: solid;
          border-color: rgb(48, 54, 61);
          padding: 1;
          margin-top: 1;
          color: rgb(230, 237, 243);
        }
      </style>
    )html";

  TabsDemo() { Bind(current_tab); }
};

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

<dialog> ​

  • Classification: Modal Overlay (rtxui::dialog)
  • Layer: Top-layer modal (z-index: 100)

Attributes ​

AttributeTypeDefaultReactiveDescription
openbooleanfalseTwo-wayVisibility toggle. When true, presents centered overlay with translucent backdrop.
titlestring"Dialog"One-wayTitle text displayed in the header frame.

Keyboard Invariants: ​

Pressing the Escape key automatically dismisses an open dialog and updates the bound open property to false.

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 built-in <dialog> element.
//
// `open` is bound to a bool; the dialog renders centered over the rest of the
// UI with a dimmed backdrop and closes on Escape.
//
// Try it: open the dialog, then dismiss it with either button or Escape.
#include <rtxui/rtxui.hpp>

using namespace rtxui;

class DialogDemo : public Component<DialogDemo> {
 public:
  bool is_modal_open = false;
  std::string last_action = "Nothing yet.";

  void ShowDialog() { is_modal_open = true; }

  void Proceed() {
    last_action = "Proceeded.";
    is_modal_open = false;
  }

  void Cancel() {
    last_action = "Cancelled.";
    is_modal_open = false;
  }

  std::string_view view = R"html(
      <div class="card">
        <h1>Overlays &amp; Dialogs</h1>
        <p>The dialog renders above everything else, on a dimmed backdrop.</p>

        <button onclick="ShowDialog">Open dialog</button>

        <div class="status">
          <span class="label">Last action</span>
          <span class="value">{last_action}</span>
        </div>

        <dialog open="{is_modal_open}" title="Confirm destruction">
          <p class="dialog-msg">This action is permanent and cannot be undone.</p>
          <div class="actions">
            <button class="danger" onclick="Proceed">Proceed</button>
            <button onclick="Cancel">Cancel</button>
          </div>
        </dialog>
      </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);
          --danger: rgb(248, 81, 73);

          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;
          width: 54;
        }
        h1 {
          color: var(--accent);
          font-weight: bold;
        }
        p {
          color: var(--muted);
          margin-bottom: 1;
        }

        /* Every colour is stated outright: a button that inherits the
           terminal's default foreground can end up invisible against its own
           background on a light-themed terminal. */
        button {
          border: tall;
          border-color: var(--border);
          background-color: var(--bg);
          color: var(--text);
          padding: 0 2;
          text-align: center;
          transition: background-color 0.15s ease, border-color 0.15s ease,
                      color 0.15s ease;
        }
        button:hover, button:focus {
          border-color: var(--accent);
          color: var(--accent);
        }
        button:active {
          background-color: var(--accent);
          color: var(--bg);
        }

        .status {
          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;
        }

        .dialog-msg {
          color: var(--danger);
          margin-bottom: 1;
        }
        .actions {
          display: flex;
          gap: 2;
          justify-content: flex-end;
        }
        .actions button {
          /* Without this the two buttons shrink to fit and their labels wrap
             mid-word inside the dialog's max-width. */
          flex-shrink: 0;
        }
        .actions button.danger {
          border-color: var(--danger);
          color: var(--danger);
        }
        .actions button.danger:hover, .actions button.danger:focus {
          background-color: var(--danger);
          color: var(--bg);
        }
      </style>
    )html";

  DialogDemo() {
    Bind(is_modal_open);
    Bind(last_action);
    Bind(ShowDialog);
    Bind(Proceed);
    Bind(Cancel);
  }
};

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

<tooltip> ​

  • Classification: Hover Overlay (rtxui::tooltip)
  • Layer: Top-layer popup (z-index: 1000)

Attributes ​

AttributeTypeDefaultReactiveDescription
contentstring""One-wayText rendered inside the floating popup.
placementstring"top"One-wayPositioning anchor: "top", "bottom", "left", "right", "top-start", "top-end", "bottom-start", "bottom-end".
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 <tooltip> component, shown on hover in each of four directions.
#include <rtxui/rtxui.hpp>
#include <string>

using namespace rtxui;

class TooltipDemo : public Component<TooltipDemo> {
 public:
  std::string custom_text = "Press any key to modify this text!";

  std::string_view view = R"html(
    <div class="container">
      <h2>RTXUI Tooltip Component Demo</h2>
      <p class="desc">
        Hover over the buttons below using your mouse cursor to see tooltips rendered in different directions and alignments.
      </p>

      <div class="showcase">
        <div class="section-title">Top Placements:</div>
        <div class="row">
          <tooltip content="Top Start (Left Aligned)" placement="top-start">
            <button class="btn">top-start</button>
          </tooltip>
          <tooltip content="Top Center (Centered)" placement="top">
            <button class="btn">top (center)</button>
          </tooltip>
          <tooltip content="Top End (Right Aligned)" placement="top-end">
            <button class="btn">top-end</button>
          </tooltip>
        </div>

        <div class="section-title">Bottom Placements:</div>
        <div class="row">
          <tooltip content="Bottom Start (Left Aligned)" placement="bottom-start">
            <button class="btn">bottom-start</button>
          </tooltip>
          <tooltip content="Bottom Center (Centered)" placement="bottom">
            <button class="btn">bottom (center)</button>
          </tooltip>
          <tooltip content="Bottom End (Right Aligned)" placement="bottom-end">
            <button class="btn">bottom-end</button>
          </tooltip>
        </div>

        <div class="section-title">Left Placements:</div>
        <div class="row">
          <tooltip content="Left Start (Top Aligned)" placement="left-start">
            <button class="btn tall-btn">left-start</button>
          </tooltip>
          <tooltip content="Left Center (Centered)" placement="left">
            <button class="btn tall-btn">left (center)</button>
          </tooltip>
          <tooltip content="Left End (Bottom Aligned)" placement="left-end">
            <button class="btn tall-btn">left-end</button>
          </tooltip>
        </div>

        <div class="section-title">Right Placements:</div>
        <div class="row">
          <tooltip content="Right Start (Top Aligned)" placement="right-start">
            <button class="btn tall-btn">right-start</button>
          </tooltip>
          <tooltip content="Right Center (Centered)" placement="right">
            <button class="btn tall-btn">right (center)</button>
          </tooltip>
          <tooltip content="Right End (Bottom Aligned)" placement="right-end">
            <button class="btn tall-btn">right-end</button>
          </tooltip>
        </div>

        <div class="section-title">Dynamic Tooltip:</div>
        <div class="row center-row">
          <tooltip content="{custom_text}" placement="top">
            <button class="btn highlight-btn">Dynamic Text Tooltip (top)</button>
          </tooltip>
        </div>

        <div class="input-section">
          <p>Customize Dynamic Tooltip Text:</p>
          <input value="{custom_text}" />
        </div>
      </div>
    </div>

    <style>
      self {
        --muted: rgb(139, 148, 158);
        --accent: rgb(88, 166, 255);

        display: block;
        padding: 2;
        background-color: rgb(13, 17, 23);
        color: rgb(230, 237, 243);
      }
      h2 {
        color: var(--accent);
        font-weight: bold;
        margin-bottom: 0;
      }
      .desc {
        color: var(--muted);
        margin-bottom: 2;
      }
      .showcase {
        max-width: 80;
        margin: auto;
        display: flex;
        flex-direction: column;
        gap: 1;
        width: 76;
      }
      .section-title {
        color: var(--accent);
        font-weight: bold;
        margin-top: 1;
        margin-bottom: 0;
      }
      .row {
        display: flex;
        flex-direction: row;
        justify-content: space-between;
        align-items: center;
        width: 100%;
        margin-bottom: 1;
      }
      .btn {
        background-color: rgb(22, 27, 34);
        color: white;
        cursor: pointer;
        padding: 0 1;
      }
      .btn:hover {
        background-color: var(--accent);
      }
      .tall-btn {
        height: 3;
      }
      .highlight-btn {
        color: rgb(254, 240, 138);
      }
      .highlight-btn:hover {
        background-color: rgb(234, 179, 8);
        color: black;
      }
      .input-section {
        display: block;
        margin-top: 1;
        padding-top: 1;
      }
      .input-section p {
        color: var(--muted);
        margin-bottom: 1;
      }
      input {
        width: 100%;
        padding-left: 1;
      }
    </style>
  )html";

  TooltipDemo() {
    Bind(custom_text);
    EnableHotReload();
  }
};

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

6. Typography & Formatting Elements ​

TagClassificationDefault StyleSemantic Invariant
<h1>, <h2>, <h3>, <h4>, <h5>, <h6>Blockdisplay: block; font-weight: bold;Hierarchical headings with stepped margins and sizes.
<p>Blockdisplay: block; margin: 1 0;Paragraph block with vertical separation.
<a>Inlinecolor: rgb(59, 130, 246); text-decoration: underline;Hyperlink. href="#id" triggers page-local scroll-into-view.
<b>, <strong>Inlinefont-weight: bold;Emphasized text with bold weight attribute.
<i>, <em>Inlinefont-style: italic;Slanted text with italic terminal attribute.
<u>Inlinetext-decoration: underline;Underlined text.
<s>, <strike>, <del>Inlinetext-decoration: line-through;Strikethrough text.
<code>Inlinebackground-color: rgb(40, 40, 40);Monospaced inline code snippet.
<kbd>Inlinedisplay: inline; font-weight: bold;Keyboard shortcut token.
<pre>Blockwhite-space: pre;Preformatted text preserving line breaks and spaces.
<blockquote>Blockdisplay: block; padding-left: 2; border-left: solid;Indented quotation block with left accent line.
<hr />Blockheight: 1; border-top: solid;Full-width horizontal divider line.
<br />Inline—Forces hard line break in inline flow algorithms.

<markdown> ​

  • Classification: Block Component (rtxui::markdown)

Attributes ​

AttributeTypeDefaultReactiveDescription
contentstring""Two-wayRaw Markdown text to parse and render.
stylesheetstring""One-wayCustom CSS applied to the rendered DOM tree.
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 <hr> separator, and how border styles apply to it.
#include <rtxui/rtxui.hpp>

using namespace rtxui;

class HrDemo : public Component<HrDemo> {
 public:
  std::string_view view = R"html(
      <div class="container">
        <p class="title">Horizontal Rule Element Demo</p>
        <p class="desc">The &lt;hr&gt; component renders a light box-drawing line horizontally, acting as a section separator.</p>
        
        <p class="section-title">Section 1: Introduction</p>
        <p class="content">This is the first section of the document. Below it is a horizontal separator rule.</p>
        
        <hr />
        
        <p class="section-title">Section 2: Layout & Separation</p>
        <p class="content">This is the second section of the document, neatly separated from the introduction above.</p>
      </div>

      <style>
        self {

          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: rgb(139, 148, 158);
          margin-bottom: 2;
        }
        .section-title {
          display: block;
          font-weight: bold;
          color: rgb(56, 189, 248);
          margin-top: 1;
        }
        .content {
          display: block;
          color: rgb(229, 231, 235);
        }
      </style>
    )html";
};

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

7. Lists (<ul>, <ol>, <li>) ​

Unordered Lists (<ul>) ​

Unordered lists render alternating glyph markers based on structural nesting depth:

  • Level 1: Disc (• )
  • Level 2: Circle (○ )
  • Level 3+: Square (■ )

Ordered Lists (<ol>) ​

Ordered lists format numbered decimal prefixes (1. , 2. ):

  • start (integer, default 1): Initial counter value.
  • reversed (boolean, default false): Counts down rather than up.

List Items (<li>) ​

  • value (integer): Explicit sequence override within ordered lists.
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.
//
// <ul>, <ol> and <li>, including nesting, `start`/`reversed`, per-item `value`,
// and the list-style-type property.
#include <rtxui/rtxui.hpp>

using namespace rtxui;

class ListsDemo : public Component<ListsDemo> {
 public:
  std::string selected_style = "disc";

  std::string_view view = R"html(
      <div class="container">
        <div class="header">
          <span class="title">RTXUI List Elements Demo</span>
          <span class="desc">Showcases nested unordered lists (&lt;ul&gt;), ordered lists (&lt;ol&gt;), and dynamic styling.</span>
        </div>

        <div class="demo-grid">
          <!-- Sidebar Controls -->
          <div class="sidebar">
            <span class="section-title">Controls</span>
            <p class="control-label">Custom List Style:</p>
            <select value="{selected_style}">
              <option value="disc">disc (•)</option>
              <option value="circle">circle (○)</option>
              <option value="square">square (■)</option>
              <option value="decimal">decimal (1., 2.)</option>
              <option value="none">none</option>
            </select>
          </div>

          <!-- Live Preview -->
          <div class="preview">
            <span class="section-title">Interactive Preview</span>
            
            <p class="preview-label">1. Custom Styled List (Dynamic list-style-type)</p>
            <ul style="list-style-type: {selected_style};">
              <li>Dynamic list item A</li>
              <li>Dynamic list item B</li>
              <li>Dynamic list item C</li>
            </ul>

            <p class="preview-label">2. Default Nested List (Automatic bullet alternation)</p>
            <ul>
              <li>Level 1 (disc)
                <ul>
                  <li>Level 2 (circle)
                    <ul>
                      <li>Level 3 (square)</li>
                      <li>Level 3 item 2</li>
                    </ul>
                  </li>
                  <li>Level 2 item 2</li>
                </ul>
              </li>
              <li>Level 1 item 2</li>
            </ul>

            <p class="preview-label">3. Ordered List (Decimal numbering)</p>
            <ol>
              <li>First steps with RTXUI</li>
              <li>Writing XML-based components</li>
              <li>Applying custom CSS rules</li>
            </ol>
          </div>
        </div>
      </div>

      <style>
        self {

          display: block;
          padding: 1;
          background-color: rgb(13, 17, 23);
          color: white;
          width: 100%;
          height: 100%;
        }
        .container {
          display: block;
        }
        .header {
          display: block;
          margin-bottom: 2;
        }
        .title {
          display: block;
          font-weight: bold;
          color: rgb(56, 189, 248);
          margin-bottom: 1;
        }
        .desc {
          display: block;
          color: rgb(139, 148, 158);
        }
        .demo-grid {
          display: flex;
          flex-direction: row;
          gap: 4;
        }
        .sidebar {
          display: block;
          width: 30;
          border-right: solid;
          border-color: rgb(48, 54, 61);
          padding-right: 2;
        }
        .preview {
          display: block;
          flex: 1;
          padding-left: 2;
        }
        .section-title {
          display: block;
          font-weight: bold;
          color: rgb(244, 63, 94);
          margin-bottom: 2;
        }
        .control-label {
          display: block;
          color: rgb(203, 213, 225);
          margin-bottom: 1;
        }
        .preview-label {
          display: block;
          font-weight: bold;
          color: rgb(56, 189, 248);
          margin-top: 2;
          margin-bottom: 1;
        }
        ul, ol {
          margin-bottom: 1;
        }
      </style>
    )html";

  ListsDemo() { Bind(selected_style); }
};

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

8. Template Control Flow ​

<if>, <elif>, <else> ​

Conditional evaluation nodes. Direct children of <if> branches are mounted or unmounted dynamically:

xml
<if condition="{has_error}">
  <span class="error">{error_message}</span>
</if>
<elif condition="{is_loading}">
  <span>Loading...</span>
</elif>
<else>
  <span>Ready.</span>
</else>

<for> ​

Repeats template structures across bound collections. Syntax: <for each="{collection}" as="item">. The loop index is available as {$index} (zero-based).

  • each: Bound collection expression.
  • as: Variable alias for collection element.
  • key: (Optional) Unique identifier field on struct item. Preserves element state and cursor position across sort and filter operations.

<slot> and <template> (Content Projection) ​

Enables reusable components to project child content passed by their caller:

  • Default Slot: <slot></slot> inside a component definition renders all unassigned child elements.
  • Named Slots: <slot.header></slot.header> or <slot name="header"></slot> defines named projection targets.
  • Content Injection: Callers project into named slots via <template.header> or <template name="header">.
  • Fallback Content: Markup placed inside <slot>Fallback</slot> is rendered if the caller provides no projecting children.
xml
<!-- Component Definition (e.g. Card) -->
<div class="card">
  <div class="card-header"><slot.header>Default Header</slot.header></div>
  <div class="card-body"><slot></slot></div>
</div>

<!-- Caller Usage -->
<Card>
  <template.header>
    <span>Custom Title</span>
  </template.header>
  <p>Body projected into default slot.</p>
</Card>
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.
//
// Content projection with <slot>.
//
// A reusable Card component places its caller's markup into named slots, which
// is how every built-in tag is implemented too.
#include <rtxui/rtxui.hpp>

using namespace rtxui;

// A reusable Card component with named header/footer and a default body slot.
class Card : public Component<Card> {
 public:
  std::string_view view = R"html(
    <div class="card-border">
      <div class="card-header">
        <slot.header>Default Header</slot.header>
      </div>
      <div class="card-body">
        <slot></slot>
      </div>
      <div class="card-footer">
        <slot.footer></slot.footer>
      </div>
    </div>
    <style>
      self {
        --bg: rgb(13, 17, 23);

          background-color: var(--bg);
        display: block;
        margin: 1;
      }
      .card-border {
        display: block;
        border: solid;
        border-color: #475569;
        background-color: #0f172a;
      }
      .card-header {
        display: block;
        border-bottom: dashed;
        border-color: #334155;
        padding-left: 1;
        padding-right: 1;
        font-weight: bold;
        color: #38bdf8;
      }
      .card-body {
        display: block;
        padding: 1;
        color: #e2e8f0;
      }
      .card-footer {
        display: block;
        border-top: solid;
        border-color: #334155;
        padding-left: 1;
        padding-right: 1;
        color: #94a3b8;
      }
    </style>
  )html";
};

class SlotsDemo : public Component<SlotsDemo> {
 public:
  std::string_view view = R"html(
    <div class="container">
      <h1>Component Slots & Composition</h1>
      <p>This demo showcases how custom components can define slots for default and named child content.</p>

      <Card>
        <template.header>
          <span>Custom Card Title</span>
        </template.header>

        <div class="content-block">
          <p>This body content is passed into the default slot of the Card component.</p>
          <p>It can contain arbitrary elements, nested components, or text.</p>
        </div>

        <template.footer>
          <span>Footer: Page 1 of 1</span>
        </template.footer>
      </Card>
    </div>
    <style>
      self {
        display: block;
        padding: 1;
        background-color: var(--bg);
        color: white;
        width: 100%;
        height: 100%;
      }
      h1 {
        font-weight: bold;
        color: #38bdf8;
        margin-bottom: 1;
      }
      p {
        color: #94a3b8;
        margin-bottom: 1;
      }
      .content-block {
        display: block;
      }
    </style>
  )html";

  SlotsDemo() { Import<Card>(); }
};

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