1# Lip Gloss
2
3<p>
4 <a href="https://stuff.charm.sh/lipgloss/lipgloss-mascot-2k.png"><img width="340" alt="Lip Gloss title treatment" src="https://github.com/charmbracelet/lipgloss/assets/25087/147cadb1-4254-43ec-ae6b-8d6ca7b029a1"></a><br>
5 <a href="https://github.com/charmbracelet/lipgloss/releases"><img src="https://img.shields.io/github/release/charmbracelet/lipgloss.svg" alt="Latest Release"></a>
6 <a href="https://pkg.go.dev/github.com/charmbracelet/lipgloss?tab=doc"><img src="https://godoc.org/github.com/golang/gddo?status.svg" alt="GoDoc"></a>
7 <a href="https://github.com/charmbracelet/lipgloss/actions"><img src="https://github.com/charmbracelet/lipgloss/workflows/build/badge.svg" alt="Build Status"></a>
8 <a href="https://www.phorm.ai/query?projectId=a0e324b6-b706-4546-b951-6671ea60c13f"><img src="https://stuff.charm.sh/misc/phorm-badge.svg" alt="phorm.ai"></a>
9</p>
10
11Style definitions for nice terminal layouts. Built with TUIs in mind.
12
13
14
15Lip Gloss takes an expressive, declarative approach to terminal rendering.
16Users familiar with CSS will feel at home with Lip Gloss.
17
18```go
19
20import "github.com/charmbracelet/lipgloss"
21
22var style = lipgloss.NewStyle().
23 Bold(true).
24 Foreground(lipgloss.Color("#FAFAFA")).
25 Background(lipgloss.Color("#7D56F4")).
26 PaddingTop(2).
27 PaddingLeft(4).
28 Width(22)
29
30fmt.Println(style.Render("Hello, kitty"))
31```
32
33## Colors
34
35Lip Gloss supports the following color profiles:
36
37### ANSI 16 colors (4-bit)
38
39```go
40lipgloss.Color("5") // magenta
41lipgloss.Color("9") // red
42lipgloss.Color("12") // light blue
43```
44
45### ANSI 256 Colors (8-bit)
46
47```go
48lipgloss.Color("86") // aqua
49lipgloss.Color("201") // hot pink
50lipgloss.Color("202") // orange
51```
52
53### True Color (16,777,216 colors; 24-bit)
54
55```go
56lipgloss.Color("#0000FF") // good ol' 100% blue
57lipgloss.Color("#04B575") // a green
58lipgloss.Color("#3C3C3C") // a dark gray
59```
60
61...as well as a 1-bit ASCII profile, which is black and white only.
62
63The terminal's color profile will be automatically detected, and colors outside
64the gamut of the current palette will be automatically coerced to their closest
65available value.
66
67### Adaptive Colors
68
69You can also specify color options for light and dark backgrounds:
70
71```go
72lipgloss.AdaptiveColor{Light: "236", Dark: "248"}
73```
74
75The terminal's background color will automatically be detected and the
76appropriate color will be chosen at runtime.
77
78### Complete Colors
79
80CompleteColor specifies exact values for True Color, ANSI256, and ANSI color
81profiles.
82
83```go
84lipgloss.CompleteColor{TrueColor: "#0000FF", ANSI256: "86", ANSI: "5"}
85```
86
87Automatic color degradation will not be performed in this case and it will be
88based on the color specified.
89
90### Complete Adaptive Colors
91
92You can use `CompleteColor` with `AdaptiveColor` to specify the exact values for
93light and dark backgrounds without automatic color degradation.
94
95```go
96lipgloss.CompleteAdaptiveColor{
97 Light: CompleteColor{TrueColor: "#d7ffae", ANSI256: "193", ANSI: "11"},
98 Dark: CompleteColor{TrueColor: "#d75fee", ANSI256: "163", ANSI: "5"},
99}
100```
101
102## Inline Formatting
103
104Lip Gloss supports the usual ANSI text formatting options:
105
106```go
107var style = lipgloss.NewStyle().
108 Bold(true).
109 Italic(true).
110 Faint(true).
111 Blink(true).
112 Strikethrough(true).
113 Underline(true).
114 Reverse(true)
115```
116
117## Block-Level Formatting
118
119Lip Gloss also supports rules for block-level formatting:
120
121```go
122// Padding
123var style = lipgloss.NewStyle().
124 PaddingTop(2).
125 PaddingRight(4).
126 PaddingBottom(2).
127 PaddingLeft(4)
128
129// Margins
130var style = lipgloss.NewStyle().
131 MarginTop(2).
132 MarginRight(4).
133 MarginBottom(2).
134 MarginLeft(4)
135```
136
137There is also shorthand syntax for margins and padding, which follows the same
138format as CSS:
139
140```go
141// 2 cells on all sides
142lipgloss.NewStyle().Padding(2)
143
144// 2 cells on the top and bottom, 4 cells on the left and right
145lipgloss.NewStyle().Margin(2, 4)
146
147// 1 cell on the top, 4 cells on the sides, 2 cells on the bottom
148lipgloss.NewStyle().Padding(1, 4, 2)
149
150// Clockwise, starting from the top: 2 cells on the top, 4 on the right, 3 on
151// the bottom, and 1 on the left
152lipgloss.NewStyle().Margin(2, 4, 3, 1)
153```
154
155## Aligning Text
156
157You can align paragraphs of text to the left, right, or center.
158
159```go
160var style = lipgloss.NewStyle().
161 Width(24).
162 Align(lipgloss.Left). // align it left
163 Align(lipgloss.Right). // no wait, align it right
164 Align(lipgloss.Center) // just kidding, align it in the center
165```
166
167## Width and Height
168
169Setting a minimum width and height is simple and straightforward.
170
171```go
172var style = lipgloss.NewStyle().
173 SetString("What’s for lunch?").
174 Width(24).
175 Height(32).
176 Foreground(lipgloss.Color("63"))
177```
178
179## Borders
180
181Adding borders is easy:
182
183```go
184// Add a purple, rectangular border
185var style = lipgloss.NewStyle().
186 BorderStyle(lipgloss.NormalBorder()).
187 BorderForeground(lipgloss.Color("63"))
188
189// Set a rounded, yellow-on-purple border to the top and left
190var anotherStyle = lipgloss.NewStyle().
191 BorderStyle(lipgloss.RoundedBorder()).
192 BorderForeground(lipgloss.Color("228")).
193 BorderBackground(lipgloss.Color("63")).
194 BorderTop(true).
195 BorderLeft(true)
196
197// Make your own border
198var myCuteBorder = lipgloss.Border{
199 Top: "._.:*:",
200 Bottom: "._.:*:",
201 Left: "|*",
202 Right: "|*",
203 TopLeft: "*",
204 TopRight: "*",
205 BottomLeft: "*",
206 BottomRight: "*",
207}
208```
209
210There are also shorthand functions for defining borders, which follow a similar
211pattern to the margin and padding shorthand functions.
212
213```go
214// Add a thick border to the top and bottom
215lipgloss.NewStyle().
216 Border(lipgloss.ThickBorder(), true, false)
217
218// Add a double border to the top and left sides. Rules are set clockwise
219// from top.
220lipgloss.NewStyle().
221 Border(lipgloss.DoubleBorder(), true, false, false, true)
222```
223
224For more on borders see [the docs][docs].
225
226## Copying Styles
227
228Just use assignment:
229
230```go
231style := lipgloss.NewStyle().Foreground(lipgloss.Color("219"))
232
233copiedStyle := style // this is a true copy
234
235wildStyle := style.Blink(true) // this is also true copy, with blink added
236
237```
238
239Since `Style` data structures contains only primitive types, assigning a style
240to another effectively creates a new copy of the style without mutating the
241original.
242
243## Inheritance
244
245Styles can inherit rules from other styles. When inheriting, only unset rules
246on the receiver are inherited.
247
248```go
249var styleA = lipgloss.NewStyle().
250 Foreground(lipgloss.Color("229")).
251 Background(lipgloss.Color("63"))
252
253// Only the background color will be inherited here, because the foreground
254// color will have been already set:
255var styleB = lipgloss.NewStyle().
256 Foreground(lipgloss.Color("201")).
257 Inherit(styleA)
258```
259
260## Unsetting Rules
261
262All rules can be unset:
263
264```go
265var style = lipgloss.NewStyle().
266 Bold(true). // make it bold
267 UnsetBold(). // jk don't make it bold
268 Background(lipgloss.Color("227")). // yellow background
269 UnsetBackground() // never mind
270```
271
272When a rule is unset, it won't be inherited or copied.
273
274## Enforcing Rules
275
276Sometimes, such as when developing a component, you want to make sure style
277definitions respect their intended purpose in the UI. This is where `Inline`
278and `MaxWidth`, and `MaxHeight` come in:
279
280```go
281// Force rendering onto a single line, ignoring margins, padding, and borders.
282someStyle.Inline(true).Render("yadda yadda")
283
284// Also limit rendering to five cells
285someStyle.Inline(true).MaxWidth(5).Render("yadda yadda")
286
287// Limit rendering to a 5x5 cell block
288someStyle.MaxWidth(5).MaxHeight(5).Render("yadda yadda")
289```
290
291## Tabs
292
293The tab character (`\t`) is rendered differently in different terminals (often
294as 8 spaces, sometimes 4). Because of this inconsistency, Lip Gloss converts
295tabs to 4 spaces at render time. This behavior can be changed on a per-style
296basis, however:
297
298```go
299style := lipgloss.NewStyle() // tabs will render as 4 spaces, the default
300style = style.TabWidth(2) // render tabs as 2 spaces
301style = style.TabWidth(0) // remove tabs entirely
302style = style.TabWidth(lipgloss.NoTabConversion) // leave tabs intact
303```
304
305## Rendering
306
307Generally, you just call the `Render(string...)` method on a `lipgloss.Style`:
308
309```go
310style := lipgloss.NewStyle().Bold(true).SetString("Hello,")
311fmt.Println(style.Render("kitty.")) // Hello, kitty.
312fmt.Println(style.Render("puppy.")) // Hello, puppy.
313```
314
315But you could also use the Stringer interface:
316
317```go
318var style = lipgloss.NewStyle().SetString("你好,猫咪。").Bold(true)
319fmt.Println(style) // 你好,猫咪。
320```
321
322## Utilities
323
324In addition to pure styling, Lip Gloss also ships with some utilities to help
325assemble your layouts.
326
327### Joining Paragraphs
328
329Horizontally and vertically joining paragraphs is a cinch.
330
331```go
332// Horizontally join three paragraphs along their bottom edges
333lipgloss.JoinHorizontal(lipgloss.Bottom, paragraphA, paragraphB, paragraphC)
334
335// Vertically join two paragraphs along their center axes
336lipgloss.JoinVertical(lipgloss.Center, paragraphA, paragraphB)
337
338// Horizontally join three paragraphs, with the shorter ones aligning 20%
339// from the top of the tallest
340lipgloss.JoinHorizontal(0.2, paragraphA, paragraphB, paragraphC)
341```
342
343### Measuring Width and Height
344
345Sometimes you’ll want to know the width and height of text blocks when building
346your layouts.
347
348```go
349// Render a block of text.
350var style = lipgloss.NewStyle().
351 Width(40).
352 Padding(2)
353var block string = style.Render(someLongString)
354
355// Get the actual, physical dimensions of the text block.
356width := lipgloss.Width(block)
357height := lipgloss.Height(block)
358
359// Here's a shorthand function.
360w, h := lipgloss.Size(block)
361```
362
363### Placing Text in Whitespace
364
365Sometimes you’ll simply want to place a block of text in whitespace.
366
367```go
368// Center a paragraph horizontally in a space 80 cells wide. The height of
369// the block returned will be as tall as the input paragraph.
370block := lipgloss.PlaceHorizontal(80, lipgloss.Center, fancyStyledParagraph)
371
372// Place a paragraph at the bottom of a space 30 cells tall. The width of
373// the text block returned will be as wide as the input paragraph.
374block := lipgloss.PlaceVertical(30, lipgloss.Bottom, fancyStyledParagraph)
375
376// Place a paragraph in the bottom right corner of a 30x80 cell space.
377block := lipgloss.Place(30, 80, lipgloss.Right, lipgloss.Bottom, fancyStyledParagraph)
378```
379
380You can also style the whitespace. For details, see [the docs][docs].
381
382## Rendering Tables
383
384Lip Gloss ships with a table rendering sub-package.
385
386```go
387import "github.com/charmbracelet/lipgloss/table"
388```
389
390Define some rows of data.
391
392```go
393rows := [][]string{
394 {"Chinese", "您好", "你好"},
395 {"Japanese", "こんにちは", "やあ"},
396 {"Arabic", "أهلين", "أهلا"},
397 {"Russian", "Здравствуйте", "Привет"},
398 {"Spanish", "Hola", "¿Qué tal?"},
399}
400```
401
402Use the table package to style and render the table.
403
404```go
405var (
406 purple = lipgloss.Color("99")
407 gray = lipgloss.Color("245")
408 lightGray = lipgloss.Color("241")
409
410 headerStyle = lipgloss.NewStyle().Foreground(purple).Bold(true).Align(lipgloss.Center)
411 cellStyle = lipgloss.NewStyle().Padding(0, 1).Width(14)
412 oddRowStyle = cellStyle.Foreground(gray)
413 evenRowStyle = cellStyle.Foreground(lightGray)
414)
415
416t := table.New().
417 Border(lipgloss.NormalBorder()).
418 BorderStyle(lipgloss.NewStyle().Foreground(purple)).
419 StyleFunc(func(row, col int) lipgloss.Style {
420 switch {
421 case row == table.HeaderRow:
422 return headerStyle
423 case row%2 == 0:
424 return evenRowStyle
425 default:
426 return oddRowStyle
427 }
428 }).
429 Headers("LANGUAGE", "FORMAL", "INFORMAL").
430 Rows(rows...)
431
432// You can also add tables row-by-row
433t.Row("English", "You look absolutely fabulous.", "How's it going?")
434```
435
436Print the table.
437
438```go
439fmt.Println(t)
440```
441
442
443
444> [!WARNING]
445> Table `Rows` need to be declared before `YOffset` otherwise it does nothing.
446
447### Table Borders
448
449There are helpers to generate tables in markdown or ASCII style:
450
451#### Markdown Table
452
453```go
454table.New().Border(lipgloss.MarkdownBorder()).BorderTop(false).BorderBottom(false)
455```
456
457```
458| LANGUAGE | FORMAL | INFORMAL |
459|----------|--------------|-----------|
460| Chinese | Nǐn hǎo | Nǐ hǎo |
461| French | Bonjour | Salut |
462| Russian | Zdravstvuyte | Privet |
463| Spanish | Hola | ¿Qué tal? |
464```
465
466#### ASCII Table
467
468```go
469table.New().Border(lipgloss.ASCIIBorder())
470```
471
472```
473+----------+--------------+-----------+
474| LANGUAGE | FORMAL | INFORMAL |
475+----------+--------------+-----------+
476| Chinese | Nǐn hǎo | Nǐ hǎo |
477| French | Bonjour | Salut |
478| Russian | Zdravstvuyte | Privet |
479| Spanish | Hola | ¿Qué tal? |
480+----------+--------------+-----------+
481```
482
483For more on tables see [the docs](https://pkg.go.dev/github.com/charmbracelet/lipgloss?tab=doc) and [examples](https://github.com/charmbracelet/lipgloss/tree/master/examples/table).
484
485## Rendering Lists
486
487Lip Gloss ships with a list rendering sub-package.
488
489```go
490import "github.com/charmbracelet/lipgloss/list"
491```
492
493Define a new list.
494
495```go
496l := list.New("A", "B", "C")
497```
498
499Print the list.
500
501```go
502fmt.Println(l)
503
504// • A
505// • B
506// • C
507```
508
509Lists have the ability to nest.
510
511```go
512l := list.New(
513 "A", list.New("Artichoke"),
514 "B", list.New("Baking Flour", "Bananas", "Barley", "Bean Sprouts"),
515 "C", list.New("Cashew Apple", "Cashews", "Coconut Milk", "Curry Paste", "Currywurst"),
516 "D", list.New("Dill", "Dragonfruit", "Dried Shrimp"),
517 "E", list.New("Eggs"),
518 "F", list.New("Fish Cake", "Furikake"),
519 "J", list.New("Jicama"),
520 "K", list.New("Kohlrabi"),
521 "L", list.New("Leeks", "Lentils", "Licorice Root"),
522)
523```
524
525Print the list.
526
527```go
528fmt.Println(l)
529```
530
531<p align="center">
532<img width="600" alt="image" src="https://github.com/charmbracelet/lipgloss/assets/42545625/0dc9f440-0748-4151-a3b0-7dcf29dfcdb0">
533</p>
534
535Lists can be customized via their enumeration function as well as using
536`lipgloss.Style`s.
537
538```go
539enumeratorStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("99")).MarginRight(1)
540itemStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("212")).MarginRight(1)
541
542l := list.New(
543 "Glossier",
544 "Claire’s Boutique",
545 "Nyx",
546 "Mac",
547 "Milk",
548 ).
549 Enumerator(list.Roman).
550 EnumeratorStyle(enumeratorStyle).
551 ItemStyle(itemStyle)
552```
553
554Print the list.
555
556<p align="center">
557<img width="600" alt="List example" src="https://github.com/charmbracelet/lipgloss/assets/42545625/360494f1-57fb-4e13-bc19-0006efe01561">
558</p>
559
560In addition to the predefined enumerators (`Arabic`, `Alphabet`, `Roman`, `Bullet`, `Tree`),
561you may also define your own custom enumerator:
562
563```go
564l := list.New("Duck", "Duck", "Duck", "Duck", "Goose", "Duck", "Duck")
565
566func DuckDuckGooseEnumerator(l list.Items, i int) string {
567 if l.At(i).Value() == "Goose" {
568 return "Honk →"
569 }
570 return ""
571}
572
573l = l.Enumerator(DuckDuckGooseEnumerator)
574```
575
576Print the list:
577
578<p align="center">
579<img width="600" alt="image" src="https://github.com/charmbracelet/lipgloss/assets/42545625/157aaf30-140d-4948-9bb4-dfba46e5b87e">
580</p>
581
582If you need, you can also build lists incrementally:
583
584```go
585l := list.New()
586
587for i := 0; i < repeat; i++ {
588 l.Item("Lip Gloss")
589}
590```
591
592## Rendering Trees
593
594Lip Gloss ships with a tree rendering sub-package.
595
596```go
597import "github.com/charmbracelet/lipgloss/tree"
598```
599
600Define a new tree.
601
602```go
603t := tree.Root(".").
604 Child("A", "B", "C")
605```
606
607Print the tree.
608
609```go
610fmt.Println(t)
611
612// .
613// ├── A
614// ├── B
615// └── C
616```
617
618Trees have the ability to nest.
619
620```go
621t := tree.Root(".").
622 Child("macOS").
623 Child(
624 tree.New().
625 Root("Linux").
626 Child("NixOS").
627 Child("Arch Linux (btw)").
628 Child("Void Linux"),
629 ).
630 Child(
631 tree.New().
632 Root("BSD").
633 Child("FreeBSD").
634 Child("OpenBSD"),
635 )
636```
637
638Print the tree.
639
640```go
641fmt.Println(t)
642```
643
644<p align="center">
645<img width="663" alt="Tree Example (simple)" src="https://github.com/user-attachments/assets/5ef14eb8-a5d4-4f94-8834-e15d1e714f89">
646</p>
647
648Trees can be customized via their enumeration function as well as using
649`lipgloss.Style`s.
650
651```go
652enumeratorStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("63")).MarginRight(1)
653rootStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("35"))
654itemStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("212"))
655
656t := tree.
657 Root("⁜ Makeup").
658 Child(
659 "Glossier",
660 "Fenty Beauty",
661 tree.New().Child(
662 "Gloss Bomb Universal Lip Luminizer",
663 "Hot Cheeks Velour Blushlighter",
664 ),
665 "Nyx",
666 "Mac",
667 "Milk",
668 ).
669 Enumerator(tree.RoundedEnumerator).
670 EnumeratorStyle(enumeratorStyle).
671 RootStyle(rootStyle).
672 ItemStyle(itemStyle)
673```
674
675Print the tree.
676
677<p align="center">
678<img width="663" alt="Tree Example (makeup)" src="https://github.com/user-attachments/assets/06d12d87-744a-4c89-bd98-45de9094a97e">
679</p>
680
681The predefined enumerators for trees are `DefaultEnumerator` and `RoundedEnumerator`.
682
683If you need, you can also build trees incrementally:
684
685```go
686t := tree.New()
687
688for i := 0; i < repeat; i++ {
689 t.Child("Lip Gloss")
690}
691```
692
693---
694
695## FAQ
696
697<details>
698<summary>
699Why are things misaligning? Why are borders at the wrong widths?
700</summary>
701<p>This is most likely due to your locale and encoding, particularly with
702regard to Chinese, Japanese, and Korean (for example, <code>zh_CN.UTF-8</code>
703or <code>ja_JP.UTF-8</code>). The most direct way to fix this is to set
704<code>RUNEWIDTH_EASTASIAN=0</code> in your environment.</p>
705
706<p>For details see <a href="https://github.com/charmbracelet/lipgloss/issues/40">https://github.com/charmbracelet/lipgloss/issues/40.</a></p>
707</details>
708
709<details>
710<summary>
711Why isn't Lip Gloss displaying colors?
712</summary>
713<p>Lip Gloss automatically degrades colors to the best available option in the
714given terminal, and if output's not a TTY it will remove color output entirely.
715This is common when running tests, CI, or when piping output elsewhere.</p>
716
717<p>If necessary, you can force a color profile in your tests with
718<a href="https://pkg.go.dev/github.com/charmbracelet/lipgloss#SetColorProfile"><code>SetColorProfile</code></a>.</p>
719
720```go
721import (
722 "github.com/charmbracelet/lipgloss"
723 "github.com/muesli/termenv"
724)
725
726lipgloss.SetColorProfile(termenv.TrueColor)
727```
728
729_Note:_ this option limits the flexibility of your application and can cause
730ANSI escape codes to be output in cases where that might not be desired. Take
731careful note of your use case and environment before choosing to force a color
732profile.
733
734</details>
735
736## What about [Bubble Tea][tea]?
737
738Lip Gloss doesn’t replace Bubble Tea. Rather, it is an excellent Bubble Tea
739companion. It was designed to make assembling terminal user interface views as
740simple and fun as possible so that you can focus on building your application
741instead of concerning yourself with low-level layout details.
742
743In simple terms, you can use Lip Gloss to help build your Bubble Tea views.
744
745[tea]: https://github.com/charmbracelet/tea
746
747## Under the Hood
748
749Lip Gloss is built on the excellent [Termenv][termenv] and [Reflow][reflow]
750libraries which deal with color and ANSI-aware text operations, respectively.
751For many use cases Termenv and Reflow will be sufficient for your needs.
752
753[termenv]: https://github.com/muesli/termenv
754[reflow]: https://github.com/muesli/reflow
755
756## Rendering Markdown
757
758For a more document-centric rendering solution with support for things like
759lists, tables, and syntax-highlighted code have a look at [Glamour][glamour],
760the stylesheet-based Markdown renderer.
761
762[glamour]: https://github.com/charmbracelet/glamour
763
764## Contributing
765
766See [contributing][contribute].
767
768[contribute]: https://github.com/charmbracelet/lipgloss/contribute
769
770## Feedback
771
772We’d love to hear your thoughts on this project. Feel free to drop us a note!
773
774- [Twitter](https://twitter.com/charmcli)
775- [The Fediverse](https://mastodon.social/@charmcli)
776- [Discord](https://charm.sh/chat)
777
778## License
779
780[MIT](https://github.com/charmbracelet/lipgloss/raw/master/LICENSE)
781
782---
783
784Part of [Charm](https://charm.sh).
785
786<a href="https://charm.sh/"><img alt="The Charm logo" src="https://stuff.charm.sh/charm-badge.jpg" width="400"></a>
787
788Charm热爱开源 • Charm loves open source
789
790[docs]: https://pkg.go.dev/github.com/charmbracelet/lipgloss?tab=doc
791[wish]: https://github.com/charmbracelet/wish
792[ssh-example]: examples/ssh