Box CLI Maker is a Go library for rendering highly customizable boxes in the terminal.
Used by
kubernetes/minikube · Featured in Golang Weekly (×3) and GitHub's Release Radar
- 9 built‑in styles (Single, Double, Round, Bold, SingleDouble, DoubleSingle, Classic, Hidden, Block)
- Custom glyphs for all corners and edges
- Title positions: Inside, Top, Bottom
- Title and Content alignment: Left, Center, Right
- Inner padding and outer margin
- Optional content wrapping with
WrapContentandWrapLimit - Color support with:
- First 16 ANSI color names
#RGB,#RRGGBB,rgb:RRRR/GGGG/BBBB,rgba:RRRR/GGGG/BBBB/AAAA- Automatic conversion to the terminal's color capability; suppressed for
NO_COLORand piped output
- Unicode and emoji support with proper width handling
- Derived styles with
Copy()for building box families from a shared base - Explicit errors from
Render, plusMustRenderfor panic‑on‑error
go get github.com/box-cli-maker/box-cli-maker/v3package main
import (
"fmt"
box "github.com/box-cli-maker/box-cli-maker/v3"
)
func main() {
b := box.NewBox().
Style(box.Single). // single-line border
Padding(2, 1). // inner padding: x (horizontal), y (vertical)
Margin(3, 1). // outer margin: x (horizontal), y (vertical)
TitlePosition(box.Top).
ContentAlign(box.Center).
Color(box.Cyan).
TitleColor(box.BrightYellow)
out, err := b.Render("Box CLI Maker", "Render highly customizable boxes\nin the terminal")
if err != nil {
panic(err)
}
fmt.Println(out)
}NewBox constructs a box with the default Single style.
Configure it via fluent methods, then call Render (or MustRender) to get the box as a string.
b := box.NewBox()You can clone a configured box and tweak it:
base := box.NewBox().
Style(box.Single).
Padding(2, 1).
ContentAlign(box.Left)
info := base.Copy().Color(box.Green)
warn := base.Copy().Color(box.Yellow)Select a built‑in style:
b.Style(box.Double)You can override any glyph after choosing a style:
b.Style(box.Single).
TopLeft("+").
TopRight("+").
BottomLeft("+").
BottomRight("+").
Horizontal("-").
Vertical("|")Title position:
TitlePosition decides where the title goes: inside the box, on the top border, or on the bottom border. The default is box.Inside.
b.TitlePosition(box.Inside)
b.TitlePosition(box.Top)
b.TitlePosition(box.Bottom)Title alignment:
TitleAlign decides where the title sits: across the box for Inside titles, or along the border for Top and Bottom titles. It works together with TitlePosition, and the two can be called in any order:
b.TitlePosition(box.Top).TitleAlign(box.Center) // title centered on the top borderThe values are box.Left, box.Center, and box.Right. If you don't set it, Inside titles are centered and Top/Bottom titles are left‑aligned.
Content alignment:
ContentAlign decides whether content lines sit on the left, in the center, or on the right of the box. The box is as wide as its longest line, so it's the shorter lines that move. The default is box.Left.
b.ContentAlign(box.Left)
b.ContentAlign(box.Center)
b.ContentAlign(box.Right)Padding adds space inside the box borders, between the border and the content: px columns of spaces on both the left and right of every line, and py blank rows above and below the content.
b.Padding(px, py) // set both: horizontal (px), then vertical (py)
b.HPadding(px) // horizontal only
b.VPadding(py) // vertical onlyHorizontal comes first — the reverse of CSS's padding: vertical horizontal shorthand.
Padding defaults to 0. Setting negative padding causes Render to return an error.
Margin adds space outside the box borders — horizontal margin prepends spaces to every line, vertical margin adds blank lines above and below.
b.Margin(mx, my) // horizontal (mx) and vertical (my) margin
b.HMargin(mx) // horizontal only
b.VMargin(my) // vertical onlyThe argument order matches Padding: horizontal first.
Margin defaults to 0. Setting negative margin causes Render to return an error.
Long content can wrap automatically to fit the terminal, or at an exact width with WrapLimit. Wrapping is off by default.
b.WrapContent(true) // enable wrapping (default: 2/3 of terminal width, minus any HMargin)
b.WrapLimit(40) // set explicit wrap width (enables wrapping)
b.WrapContent(false) // disable wrappingTabs are expanded (at 8‑column stops) before wrapping, so the configured limit is honored even for tab‑heavy content.
Render returns an error if the wrap limit is not positive or the terminal width cannot be determined when wrapping is enabled without a limit.
Colors can be applied to:
- Title:
TitleColor - Content:
ContentColor - Border:
Color
Accepted formats:
- The 16 ANSI color names:
box.Black,box.Red,box.Green,box.Yellow,box.Blue,box.Magenta,box.Cyan,box.White— each also available with aBrightprefix (e.g.box.BrightYellow) or itsHialias (e.g.box.HiRed). - Hex and XParseColor formats (TrueColor and 8-bit):
#RGB,#RRGGBB,rgb:RRRR/GGGG/BBBB,rgba:RRRR/GGGG/BBBB/AAAA
Example:
b.TitleColor(box.BrightYellow)
b.ContentColor("#00FF00")
b.Color("rgb:0000/ffff/0000")Invalid colors cause Render to return an error.
Colors automatically adapt to what the terminal supports (TrueColor, 256‑color, or 16‑color). When the output can't show color at all — NO_COLOR set, TERM=dumb, or output piped to a file — colors are dropped entirely, so logs stay free of escape codes.
Your text may already be styled before it reaches the box — by your logger, a color library, or your own escape codes. That's fine: pass it in as-is, and the box renders correctly around it.
styled := "\x1b[31mthis red part is long enough to wrap\x1b[0m and this part is plain"
out, _ := box.NewBox().WrapLimit(24).Render("", styled)What you can rely on:
- Colors and styles in your text never leak into the borders or padding.
- Styling survives wrapping: when a colored sentence breaks across lines, every line keeps its color.
- Clickable hyperlinks (OSC 8) stay clickable — and the borders never become part of the link.
- This works together with
Color/ContentColor: your styling and the box's styling don't fight.
There is nothing to enable — this is how Render always behaves. (For the curious: every rendered line is made self‑contained by closing any open styling at the line's end and re‑opening it on the next line.)
out, err := b.Render("Title", "Content")
if err != nil {
// handle invalid style, colors, padding, wrapping, etc.
}
fmt.Println(out)Render returns an error if:
- The
BoxStyleis invalid - The
TitlePositionis invalid - The
TitleAlignorContentAlignis invalid - The wrap limit is not positive
- Padding or margin is negative
- A multiline title is used with a non‑
Insidetitle position - Any configured colors are invalid
- Terminal width detection fails when needed for wrapping
For convenience:
out := b.MustRender("Title", "Content") // panics on errorThe examples directory contains small, focused programs that showcase different features:
simple_box– minimal single box with title and content.content_align– compareLeft,Center, andRightcontent alignment.content_wrap– demonstrateWrapContent/WrapLimitwith long text.titles– every title position (Inside,Top,Bottom) with every alignment (Left,Center,Right).spacing– inner padding vs outer margin, separately and combined.box_styles– render all built‑in border styles and colors.custom_box– build boxes using fully custom corner/edge glyphs.ansi_styles_and_links– use bold/underline/blink/strikethrough and OSC 8 hyperlinks.colors_and_unicode– mix hex/ANSI colors with CJK, emoji, and wrapping.ansi_art– render more decorative/"artistic" boxes.shared_styles– derive multiple boxes from a shared base style withCopy.ksctl– real‑world example from ksctl showing wide titles vs narrow content.lolcat– rainbow color demo using custom ANSI styling helpers.readme– code used to generate the screenshot at the top of this README.
This library uses mattn/go-runewidth and github.com/charmbracelet/x/ansi to handle:
- Wide characters (e.g., CJK)
- Emojis and other multi‑cell glyphs
- Stripping ANSI sequences when measuring widths
Note:
- Rendering quality depends on the terminal emulator and font. Some combinations may misalign visually.
- Indic scripts and complex text may not display correctly in most terminals.
- Online playgrounds and many CI environments often use basic fonts and may not render Unicode/emoji correctly, so boxes may look misaligned there.
v3 is a new major version with a redesigned API.
Key changes:
-
Configstruct andNew(Config)have been replaced with:b := box.NewBox(). Style(box.Single). Padding(2, 1). TitlePosition(box.Top). ContentAlign(box.Left)
-
String‑based fields (
Type,ContentAlign,TitlePos) are now strongly typed:"Single"→box.Single"Top"→box.Top"Center"→box.Center
-
Colors:
- No more
interface{}colors (uint,[3]uint, etc.). - Use ANSI names or the documented hex/rgb formats instead.
- Invalid colors now error at
Rendertime.
- No more
-
Print/Printlnbehavior can be replicated byfmt.Println(b.MustRender(...))or your own helper.
Read more at Migration guide.
The old v2 API remains available at:
go get github.com/Delta456/box-cli-maker/v2but is no longer actively developed.
Using Box CLI Maker in your project? Add it to the adopters list — we'd love to feature it.
Thanks to CodeRabbit for sponsoring my open source work.
Thanks to:
for inspiration, and to all contributors who have improved this library over time.
Licensed under MIT.






















