TamboUI provides a BBCode-style markup parser for creating styled Text objects from strings. This is a convenient alternative to building Line and Span objects manually.

Basic Usage

// Parse markup into styled Text
Text text = MarkupParser.parse("This is [bold]bold[/bold] and [red]red[/red].");

// Use with widgets
Paragraph paragraph = Paragraph.from(
    MarkupParser.parse("[cyan]Status:[/cyan] [bold green]Ready[/]")
);

Syntax Reference

Style Tags

Style tags use square brackets with a tag name:

[tagname]content[/tagname]

Modifier Tags

Tag Effect Aliases

[bold]

Bold text

[b]

[italic]

Italic text

[i]

[underlined]

Underlined text

[u]

[dim]

Dimmed/faint text

[reversed]

Swap foreground and background

[crossed-out]

Strikethrough text

[strikethrough], [s]

Colors

Colors can be specified the ways ColorConverter supports:

  • Named colors: red, green, blue, yellow, cyan, magenta, white, black, gray, etc.

  • Hex colors: #RGB, #RRGGBB

  • RGB colors: rgb(r,g,b)

  • Indexed colors: indexed(index)

Only named colors are treated as tags/classes; explicit colors are not possible to be styled.

Implicit Close

Close the most recent tag without naming it:

[bold]Hello[/] World

This is equivalent to:

[bold]Hello[/bold] World

Implicit close is useful for deeply nested tags:

[red][bold]Important[/][/] vs [red][bold]Important[/bold][/red]

Compound Styles

Combine multiple styles in a single tag by separating them with spaces:

[bold red]Error message[/]
[italic cyan]Hint text[/]
[bold underlined yellow]Warning![/]

All tokens in a compound style become CSS classes for targeting (see CSS Class Targeting).

Background Colors

Use on to specify a background color:

[white on blue]Highlighted text[/]
[black on yellow]Warning banner[/]
[bold red on white]Alert![/]

Create clickable links (in supported terminals):

[link=https://example.com]Click here[/link]

Escaped Brackets

Two escape mechanisms are supported for literal brackets:

Double Brackets

Use [[tag]] for markup     renders as:  Use [tag] for markup
Content with ]] closing    renders as:  Content with ] closing

Backslash Escapes

Use \[tag] for literal     renders as:  Use [tag] for literal
Content with \] closing    renders as:  Content with ] closing
Path: C:\\Users\\name      renders as:  Path: C:\Users\name

Supported backslash escapes: \[, \], \\. Other sequences (like \n) are preserved as-is.

Nesting

Tags can be nested for combined effects:

[red]This is red and [bold]this is red and bold[/bold][/red]

Custom Style Resolver

A StyleResolver lets you define custom tags that map to styles:

MarkupParser.StyleResolver resolver = tagName -> {
    switch (tagName) {
        case "keyword": return Style.EMPTY.fg(Color.CYAN).bold();
        case "string": return Style.EMPTY.fg(Color.GREEN);
        case "comment": return Style.EMPTY.fg(Color.GRAY).italic();
        case "error": return Style.EMPTY.fg(Color.WHITE).bg(Color.RED).bold();
        default: return null;
    }
};

Text code = MarkupParser.parse(
    "[keyword]public[/] [keyword]void[/] main([string]\"Hello\"[/]) [comment]// entry point[/]",
    resolver
);

Resolver Priority

The resolver has priority over built-in styles. This means you can redefine what built-in color names mean:

// Redefine "red" to mean something else in your theme
MarkupParser.StyleResolver themeResolver = tagName -> {
    if ("red".equals(tagName)) {
        return Style.EMPTY.fg(Color.hex("#ff6b6b"));  // Custom red
    }
    return null;
};

// Now [red] uses your custom color instead of Color.RED
Text text = MarkupParser.parse("[red]Custom red[/]", themeResolver);

Merging Resolver and Compound Styles

When using compound styles with a resolver, both are merged. Inline styles (from the compound) override the resolver’s base style, similar to how CSS inline styles override class styles:

MarkupParser.StyleResolver resolver = tagName -> {
    if ("error".equals(tagName)) {
        return Style.EMPTY.fg(Color.CYAN).bold();
    }
    return null;
};

// [error] alone: cyan foreground + bold (from resolver)
Text t1 = MarkupParser.parse("[error]Message[/]", resolver);

// [error on red]: cyan foreground + bold + red background
// The resolver provides the base, compound adds the background
Text t2 = MarkupParser.parse("[error on red]Message[/]", resolver);

// [error yellow]: yellow foreground + bold
// Compound's yellow overrides resolver's cyan, but bold is kept
Text t3 = MarkupParser.parse("[error yellow]Message[/]", resolver);

// [error italic]: cyan foreground + bold + italic
// Compound adds italic to resolver's style
Text t4 = MarkupParser.parse("[error italic]Message[/]", resolver);

CSS Class Targeting

All tokens in a tag become CSS classes that can be targeted with stylesheets. For compound styles, every token (except on) becomes a CSS class:

Text text = MarkupParser.parse("[error]message[/]");
// The span has Tags extension containing "error"

Text compound = MarkupParser.parse("[bold underlined yellow]Warning![/]");
// The span has Tags extension containing "bold", "underlined", "yellow"

Text background = MarkupParser.parse("[white on blue]text[/]");
// The span has Tags extension containing "white", "blue" (not "on")

This allows external CSS to target any style token:

/* Target all bold text */
.bold {
    text-style: bold;
}

/* Target all yellow text */
.yellow {
    color: #ffcc00;
}

/* Target error messages */
.error {
    color: red;
    text-style: bold;
}

Unknown Tags

Tags that aren’t recognized as built-in styles and aren’t resolved by a custom resolver are still tracked for CSS class targeting, but have no visual effect:

// "custom" is unknown, but tracked as a CSS class
Text text = MarkupParser.parse("[custom]styled by CSS[/custom]");

// Can be styled via CSS:
// .custom { color: magenta; }

Multi-line Text

Markup can span multiple lines:

Text multiline = MarkupParser.parse("""
    [bold]Header[/]

    [dim]This is a paragraph with [cyan]highlighted[/] text.[/]

    [italic]Footer note[/]
    """);

Examples

Status Messages

MarkupParser.parse("[green]SUCCESS[/] Operation completed");
MarkupParser.parse("[yellow]WARNING[/] Disk space low");
MarkupParser.parse("[bold red]ERROR[/] Connection failed");

Syntax Highlighting

MarkupParser.StyleResolver syntax = tag -> switch (tag) {
    case "kw" -> Style.EMPTY.fg(Color.MAGENTA).bold();
    case "str" -> Style.EMPTY.fg(Color.GREEN);
    case "num" -> Style.EMPTY.fg(Color.CYAN);
    case "cmt" -> Style.EMPTY.fg(Color.GRAY).italic();
    default -> null;
};

Text code = MarkupParser.parse(
    "[kw]int[/] x = [num]42[/]; [cmt]// answer[/]",
    syntax
);

Rich Notifications

MarkupParser.parse("""
    [bold white on blue] NOTICE [/]

    Your session will expire in [bold yellow]5 minutes[/].
    Please [link=https://example.com/save]save your work[/link].
    """);

Summary Table

Syntax Description Example

[tag]…​[/tag]

Explicit close

[bold]text[/bold]

[tag]…​[/]

Implicit close (most recent)

[bold]text[/]

[tag1 tag2]…​[/]

Compound styles

[bold red]text[/]

[color on bgcolor]

Background color

[white on blue]text[/]

[link=url]…​[/link]

Hyperlink

[link=https://x.com]click[/link]

[[ / ]]

Double bracket escapes

Use Use [tag]

\[ / \] / \\

Backslash escapes

\[tag\][tag]

Next Steps