@oakoliver/lipgloss
v1.0.2
Published
CSS-like terminal styling for JavaScript. Zero dependencies, multi-runtime (Node.js, Bun, Deno). Fluent API for colors, borders, padding, margins, alignment, and layout.
Maintainers
Readme
lipgloss
CSS-like terminal styling for JavaScript. Zero dependencies, multi-runtime (Node.js, Bun, Deno).
Ported from charmbracelet/lipgloss (Go) to TypeScript.
Install
npm install @oakoliver/lipgloss
# or
bun add @oakoliver/lipgloss
# or
deno add npm:@oakoliver/lipglossQuick Start
import { newStyle, normalBorder, roundedBorder, Center } from '@oakoliver/lipgloss';
// Create a style
const style = newStyle()
.bold(true)
.foreground('#FF6B6B')
.background('#2D2D2D')
.padding(1, 2)
.border(roundedBorder())
.borderForeground('#FF6B6B')
.width(40)
.align(Center);
console.log(style.render('Hello, Lipgloss!'));Features
- Fluent, immutable API — every setter returns a new
Style(safe to share and compose) - CSS-like box model — padding, margin, border, width, height, alignment
- 10 built-in border styles — normal, rounded, block, thick, double, hidden, ASCII, markdown, and more
- Full ANSI color support — basic 16, 256-color palette, and 24-bit RGB
- Text attributes — bold, italic, underline (5 styles), strikethrough, reverse, blink, faint
- Layout utilities —
joinHorizontal,joinVertical,place,placeHorizontal,placeVertical - Color utilities —
complementary,darken,lighten,isDarkColor,lightDark,alpha - Zero dependencies — pure TypeScript, works everywhere
- TypeScript-first — full type declarations included
API
Style
import { newStyle, Style } from '@oakoliver/lipgloss';
const s = newStyle();Every setter returns a new Style instance (immutable):
const base = newStyle().foreground('#7D56F4');
const title = base.bold(true).padding(0, 1);
const body = base.faint(true);Text Attributes
s.bold(true)
s.italic(true)
s.underline(true)
s.underlineStyle('curly') // 'none' | 'single' | 'double' | 'curly' | 'dotted' | 'dashed'
s.strikethrough(true)
s.reverse(true)
s.blink(true)
s.faint(true)Colors
// Hex (#RGB or #RRGGBB)
s.foreground('#ff0000')
s.background('#2D2D2D')
s.underlineColor('#00ff00')
// ANSI basic (0-15)
import { Red, BrightCyan } from '@oakoliver/lipgloss';
s.foreground(Red)
// ANSI 256 (16-255)
s.foreground(196)
// RGB object
s.foreground({ r: 255, g: 107, b: 107 })Dimensions
s.width(40) // fixed width (content wraps/pads to fit)
s.height(5) // fixed height
s.maxWidth(80) // truncate lines exceeding this
s.maxHeight(24) // truncate output exceeding this many linesAlignment
import { Left, Center, Right, Top, Bottom } from '@oakoliver/lipgloss';
s.align(Center) // horizontal only
s.align(Center, Bottom) // horizontal and vertical
s.alignHorizontal(Right)
s.alignVertical(Top)Padding & Margin (CSS shorthand)
s.padding(1) // all sides
s.padding(1, 2) // vertical, horizontal
s.padding(1, 2, 3, 4) // top, right, bottom, left
s.paddingTop(1)
s.paddingLeft(2)
s.margin(1, 2)
s.marginBackground('#333') // color the margin areaBorders
import {
normalBorder, roundedBorder, blockBorder, thickBorder,
doubleBorder, hiddenBorder, asciiBorder, markdownBorder,
outerHalfBlockBorder, innerHalfBlockBorder,
} from '@oakoliver/lipgloss';
s.border(roundedBorder()) // all sides
s.border(normalBorder(), true, false) // top+bottom only
s.border(normalBorder(), true, true, true, true) // explicit all
s.borderForeground('#FF6B6B') // all sides same color
s.borderForeground('#f00', '#0f0', '#00f', '#ff0') // CSS shorthand
s.borderBackground('#2D2D2D')Other
s.inline(true) // strip newlines
s.tabWidth(2) // tab stop width (default 4, -1 to preserve tabs)
s.transform(s => s.toUpperCase())
s.hyperlink('https://example.com')
s.setString('prefix') // prepend when rendering
s.colorWhitespace(true) // apply bg color to padding spaces
s.underlineSpaces(true)
s.strikethroughSpaces(true)Rendering
const output = style.render('Hello, World!');
console.log(output);
// render() joins multiple args with space
style.render('Hello,', 'World!');
// toString() renders the setString value
const s = newStyle().setString('Hello').bold(true);
console.log(`${s}`); // bold "Hello"Inherit
Copy style properties from another style (padding/margin excluded):
const parent = newStyle().bold(true).foreground('#FF6B6B');
const child = newStyle().italic(true).inherit(parent);
// child is now bold + italic + #FF6B6B foregroundGetters
Every property has a corresponding getter:
s.getBold() // boolean
s.getForeground() // Color | null
s.getWidth() // number
s.getPadding() // { top, right, bottom, left }
s.getHorizontalFrameSize() // margin + border + padding (horizontal)
s.getFrameSize() // { x, y }Layout
import {
joinHorizontal, joinVertical,
place, placeHorizontal, placeVertical,
Top, Bottom, Center, Left, Right,
} from '@oakoliver/lipgloss';
// Join blocks side by side (vertical alignment)
joinHorizontal(Top, blockA, blockB, blockC);
joinHorizontal(Center, blockA, blockB);
// Stack blocks vertically (horizontal alignment)
joinVertical(Left, blockA, blockB);
joinVertical(Center, blockA, blockB);
// Place content in an unstyled box
place(80, 24, Center, Center, 'Hello');
placeHorizontal(80, Center, 'Hello');
placeVertical(24, Center, 'Hello');Color Utilities
import {
parseColor, colorToRGB, isDarkColor,
lightDark, complementary, darken, lighten, alpha,
} from '@oakoliver/lipgloss';
isDarkColor('#1a1a2e') // true
isDarkColor('#ffffff') // false
const pick = lightDark(isDarkColor('#1a1a2e'));
pick('#333', '#eee') // returns '#eee' (dark background → use light color)
complementary('#ff0000') // ~cyan
darken('#ff6b6b', 0.3) // 30% darker
lighten('#333333', 0.2) // 20% lighter
alpha('#ff0000', 0.5) // { r: 128, g: 0, b: 0 }ANSI Utilities
import { stripAnsi, stringWidth, truncate, styled, SGR } from '@oakoliver/lipgloss';
stripAnsi('\x1b[1mHello\x1b[0m') // 'Hello'
stringWidth('\x1b[1mHello\x1b[0m') // 5 (ignores ANSI, handles CJK)
truncate(longString, 40) // truncate to 40 visible chars
styled('Hi', { bold: true, fg: { type: 'rgb', value: 0, r: 255, g: 0, b: 0 } })Attribution
This is a TypeScript port of charmbracelet/lipgloss, originally written in Go by Charmbracelet, Inc.. Licensed under MIT.
License
MIT
