New Upstream Release - golang-github-logrusorgru-aurora

Ready changes

Summary

Merged new upstream version: 4.0.0 (was: 3.0.0).

Resulting package

Built on 2022-12-14T18:22 (took 7m25s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-releases golang-github-logrusorgru-aurora-dev

Lintian Result

Diff

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..854d257
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,61 @@
+on:
+  push:
+    branches:
+      - master
+  pull_request:
+    branches:
+      - master
+
+name: build
+jobs:
+  lint:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Install Go
+        uses: actions/setup-go@v3
+        with:
+          go-version: 1.19
+      - name: Checkout code
+        uses: actions/checkout@v2
+      - name: Run linters
+        uses: golangci/golangci-lint-action@v3
+        with:
+          version: v1.46.2
+
+  test:
+    strategy:
+      matrix:
+        go-version: [1.19]
+        platform: [ubuntu-latest, macos-latest, windows-latest]
+    runs-on: ${{ matrix.platform }}
+    steps:
+      - name: Install Go
+        if: success()
+        uses: actions/setup-go@v3
+        with:
+          go-version: ${{ matrix.go-version }}
+      - name: Checkout code
+        uses: actions/checkout@v2
+      - name: Run tests
+        run: go test -v -covermode=count
+
+  coverage:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Install Go
+        if: success()
+        uses: actions/setup-go@v3
+        with:
+          go-version: 1.19
+      - name: Checkout code
+        uses: actions/checkout@v2
+      - name: Calc coverage
+        run: |
+          go test -v -covermode=count -coverprofile=coverage.out
+      - name: Convert coverage.out to coverage.lcov
+        uses: jandelgado/gcov2lcov-action@v1
+      - name: Coveralls
+        uses: coverallsapp/github-action@v1.1.2
+        with:
+          github-token: ${{ secrets.github_token }}
+          path-to-lcov: coverage.lcov
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 570e361..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-language: go
-go:
-  - tip
-before_install:
-  - go get github.com/axw/gocov/gocov
-  - go get github.com/mattn/goveralls
-  - go get golang.org/x/tools/cmd/cover
-script:
-  - $HOME/gopath/bin/goveralls -service=travis-ci
diff --git a/AUTHORS.md b/AUTHORS.md
index 0ee9e3e..d268f53 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -6,3 +6,4 @@ AUTHORS
 - Ousmane Traore @otraore
 - Simon Legner @simon04
 - Sevenate @sevenate
+- JP Hastings-Spital @jphastings
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ad0a202..ba5886b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,45 @@
 Changes
 =======
 
+---
+14:15:14
+Thursday, October 8, 2022
+
+New version v4. Breaking changes. [Migrate from v3](#migrate-from-v3).
+
+- Drop deprecated `Bleach` methods. Use `Reset` or `Clear` instead.
+- Drop deprecated `Brows` and `BgBrows` methods, and `BrownFg` and `BrownBg`
+  colors. Use `Yellow` variants instead.
+- Instead of `Aurora` interface introduced `Aurora` structure.
+- Instead of `Value` interface introduced `Value` structure.
+- Implemented [hyperlinks feature](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda).
+  - added `Hyperlink` method
+  - added `HyperlinkTarget` and `HyperlinkParams` methods
+  - added `HyperlinkParam` type, `IsValidHyperlinkTarget`,
+    `IsValidHyperlinkParam`, `HyperlinkID`, `HyperlinkEscape`,
+    `HyperlinkUnescape` helper functions.
+- Introduced `Config` and related functions and methods, such as `NewConfig`,
+  `WithColors`, `WithHyperlinks`.
+- Removed `NewAurora` function, new function `New` introduced.
+- Introduced global `DefaultColorizer` that used by package root methods.
+- `Sprintf` method now belongs to a colorizer and depends on its configurations.
+  For package root `Sprintf` it's the `DefaultColorizer`.
+
+Performance for all methods is almost the same. But for color- and
+format-methods `aurora` now takes less allocations. But, unfortunately, for
+`Sprintf` it takes more allocations.
+
+###### Migrate from v3
+
+1. Use `Reset` or `Clear` instead of `Bleach`.
+2. Use `Yellow` instead of `Brows`.
+3. Use `BgYellow` instead of `BgBrown`.
+4. Use `YellowFg` instead of `BrownFg`.
+5. Use `YellowBg` instead of `BrownBg`.
+6. Use `New` instead of `NewAurora`.
+7. Use `New(WithColors(false))` to disable colors.
+8. Use `New(WithHyperlinks(false))` to disable hyperlinks.
+
 ---
 16:05:02
 Thursday, July 2, 2020
diff --git a/README.md b/README.md
index fbc26bf..314a88b 100644
--- a/README.md
+++ b/README.md
@@ -3,10 +3,9 @@ Aurora
 
 [![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white)](https://pkg.go.dev/github.com/logrusorgru/aurora/v3?tab=doc)
 [![Unlicense](https://img.shields.io/badge/license-unlicense-blue.svg)](http://unlicense.org/)
-[![Build Status](https://travis-ci.org/logrusorgru/aurora.svg)](https://travis-ci.org/logrusorgru/aurora)
-[![Coverage Status](https://coveralls.io/repos/logrusorgru/aurora/badge.svg?branch=master)](https://coveralls.io/r/logrusorgru/aurora?branch=master)
+[![Build Status](https://github.com/logrusorgru/aurora/workflows/build/badge.svg)](https://github.com/logrusorgru/aurora/actions?workflow=build)
+[![Coverage Status](https://coveralls.io/repos/github/logrusorgru/aurora/badge.svg?branch=master)](https://coveralls.io/github/logrusorgru/aurora?branch=master)
 [![GoReportCard](https://goreportcard.com/badge/logrusorgru/aurora)](https://goreportcard.com/report/logrusorgru/aurora)
-[![Gitter](https://img.shields.io/badge/chat-on_gitter-46bc99.svg?logo=data:image%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTQiIHdpZHRoPSIxNCI%2BPGcgZmlsbD0iI2ZmZiI%2BPHJlY3QgeD0iMCIgeT0iMyIgd2lkdGg9IjEiIGhlaWdodD0iNSIvPjxyZWN0IHg9IjIiIHk9IjQiIHdpZHRoPSIxIiBoZWlnaHQ9IjciLz48cmVjdCB4PSI0IiB5PSI0IiB3aWR0aD0iMSIgaGVpZ2h0PSI3Ii8%2BPHJlY3QgeD0iNiIgeT0iNCIgd2lkdGg9IjEiIGhlaWdodD0iNCIvPjwvZz48L3N2Zz4%3D&logoWidth=10)](https://gitter.im/logrusorgru/aurora)
 
 Ultimate ANSI colors for Golang. The package supports Printf/Sprintf etc.
 
@@ -21,6 +20,7 @@ Ultimate ANSI colors for Golang. The package supports Printf/Sprintf etc.
   + [Printf](#printf)
   + [aurora.Sprintf](#aurorasprintf)
   + [Enable/Disable colors](#enabledisable-colors)
+  + [Hyperlinks, default colorizer, and configurations](#hyperlinks-default-colorizer-and-configurations)
 - [Chains](#chains)
 - [Colorize](#colorize)
 - [Grayscale](#grayscale)
@@ -37,7 +37,7 @@ Ultimate ANSI colors for Golang. The package supports Printf/Sprintf etc.
 
 # Installation
 
-### Version 1.x
+##### Version 1.x
 
 Using gopkg.in.
 
@@ -45,13 +45,13 @@ Using gopkg.in.
 go get -u gopkg.in/logrusorgru/aurora.v1
 ```
 
-### Version 2.x
+##### Version 2.x
 
 ```
 go get -u github.com/logrusorgru/aurora
 ```
 
-### Go modules support, version v3+
+##### Go modules support, version v3+
 
 Get
 ```
@@ -61,10 +61,18 @@ go get -u github.com/logrusorgru/aurora/v3
 The v3 was introduced to support `go.mod` and leave previous import paths as is.
 Currently, there is no changes between them (excluding the importpath's /v3 tail).
 
+##### The latest version
+
+```
+go get -u github.com/logrusorgru/aurora/v4
+```
+
+With hyperlinks.
+
 # Test
 
 ```
-go test -cover github.com/logrusorgru/aurora/v3
+go test -cover -race github.com/logrusorgru/aurora/v4
 ```
 
 Replace the import path with your, if it's different.
@@ -79,12 +87,12 @@ package main
 import (
 	"fmt"
 
-	. "github.com/logrusorgru/aurora"
+	"github.com/logrusorgru/aurora/v4"
 )
 
 func main() {
-	fmt.Println("Hello,", Magenta("Aurora"))
-	fmt.Println(Bold(Cyan("Cya!")))
+	fmt.Println("Hello,", aurora.Magenta("Aurora"))
+	fmt.Println(aurora.Bold(aurora.Cyan("Cya!")))
 }
 
 ```
@@ -99,12 +107,12 @@ package main
 import (
 	"fmt"
 
-	. "github.com/logrusorgru/aurora"
+	"github.com/logrusorgru/aurora/v4"
 )
 
 func main() {
-	fmt.Printf("Got it %d times\n", Green(1240))
-	fmt.Printf("PI is %+1.2e\n", Cyan(3.14))
+	fmt.Printf("Got it %d times\n", aurora.Green(1240))
+	fmt.Printf("PI is %+1.2e\n", aurora.Cyan(3.14))
 }
 
 ```
@@ -119,11 +127,11 @@ package main
 import (
 	"fmt"
 
-	. "github.com/logrusorgru/aurora"
+	"github.com/logrusorgru/aurora/v4"
 )
 
 func main() {
-	fmt.Println(Sprintf(Magenta("Got it %d times"), Green(1240)))
+	fmt.Println(aurora.Sprintf(aurora.Magenta("Got it %d times"), aurora.Green(1240)))
 }
 
 ```
@@ -139,17 +147,17 @@ import (
 	"fmt"
 	"flag"
 
-	"github.com/logrusorgru/aurora"
+	"github.com/logrusorgru/aurora/v4"
 )
 
 // colorizer
-var au aurora.Aurora
+var au *aurora.Aurora
 
 var colors = flag.Bool("colors", false, "enable or disable colors")
 
 func init() {
 	flag.Parse()
-	au = aurora.NewAurora(*colors)
+	au = aurora.New(WithColors(*colors))
 }
 
 func main() {
@@ -158,22 +166,53 @@ func main() {
 }
 
 ```
-Without flags: 
+Without flags:
 ![disable png](https://github.com/logrusorgru/aurora/blob/master/disable.png)
-  
+
 With `-colors` flag:
 ![enable png](https://github.com/logrusorgru/aurora/blob/master/enable.png)
 
+### Hyperlinks, default colorizer, and configurations
+
+[Hyperlinks feature description](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda).
+
+Add a red hyperlinks with text "Example" that is referencing to
+http://example.com.
+
+```go
+package main
+
+import (
+	"flag"
+	"fmt"
+
+	"github.com/logrusorgru/aurora/v4"
+)
+
+func main() {
+	var conf = aurora.NewConfig()
+	conf.AddFlags(flag.CommandLine, "prefix.")
+	flag.Parse()
+
+	aurora.DefaultColorizer = aurora.New(conf.Options()...) // set global
+
+	fmt.Println(aurora.Red("Example").Hyperlink("http://example.com/"))
+}
+```
+Depending flags:
+![depending flags png](https://github.com/logrusorgru/aurora/blob/master/aurora_hyperlinks_flags.png)
+![depending flags gif](https://github.com/logrusorgru/aurora/blob/master/aurora_hyperlinks.gif)
+
 # Chains
 
 The following samples are equal
 
 ```go
-x := BgMagenta(Bold(Red("x")))
+x := aurora.BgMagenta(aurora.Bold(aurora.Red("x")))
 ```
 
 ```go
-x := Red("x").Bold().BgMagenta()
+x := aurora.Red("x").Bold().BgMagenta()
 ```
 
 The second is more readable
@@ -192,34 +231,34 @@ func getColors() Color {
 // [...]
 
 func main() {
-	fmt.Println(Colorize("Greeting", getColors()))
+	fmt.Println(aurora.Colorize("Greeting", getColors()))
 }
 
 ```
 Less complicated example
 
 ```go
-x := Colorize("Greeting", GreenFg|GrayBg|BoldFm)
+x := aurora.Colorize("Greeting", GreenFg|GrayBg|BoldFm)
 ```
 
 Unlike other color functions and methods (such as Red/BgBlue etc)
 a `Colorize` clears previous colors
 
 ```go
-x := Red("x").Colorize(BgGreen) // will be with green background only
+x := aurora.Red("x").Colorize(BgGreen) // will be with green background only
 ```
 
 # Grayscale
 
 ```go
 fmt.Println("  ",
-	Gray(1-1, " 00-23 ").BgGray(24-1),
-	Gray(4-1, " 03-19 ").BgGray(20-1),
-	Gray(8-1, " 07-15 ").BgGray(16-1),
-	Gray(12-1, " 11-11 ").BgGray(12-1),
-	Gray(16-1, " 15-07 ").BgGray(8-1),
-	Gray(20-1, " 19-03 ").BgGray(4-1),
-	Gray(24-1, " 23-00 ").BgGray(1-1),
+	aurora.Gray(1-1, " 00-23 ").BgGray(24-1),
+	aurora.Gray(4-1, " 03-19 ").BgGray(20-1),
+	aurora.Gray(8-1, " 07-15 ").BgGray(16-1),
+	aurora.Gray(12-1, " 11-11 ").BgGray(12-1),
+	aurora.Gray(16-1, " 15-07 ").BgGray(8-1),
+	aurora.Gray(20-1, " 19-03 ").BgGray(4-1),
+	aurora.Gray(24-1, " 23-00 ").BgGray(1-1),
 )
 ```
 
@@ -236,6 +275,22 @@ Methods `Index` and `BgIndex` implements 8-bit colors.
 |     16-231     | 216 colors      |   38;5;n   |   48;5;n   |
 |    232-255     | 24 grayscale    |   38;5;n   |   48;5;n   |
 
+Example
+
+```go
+package main
+
+import (
+	"fmt"
+	"github.com/logrusorgru/aurora"
+)
+
+func main() {
+	for i := uint8(16); i <= 231; i++ {
+		fmt.Println(i, aurora.Index(i, "pew-pew"), aurora.BgIndex(i, "pew-pew"))
+	}
+}
+```
 
 # Supported colors & formats
 
@@ -295,13 +350,15 @@ package main
 import (
 	"fmt"
 
-	. "github.com/logrusorgru/aurora"
+	"github.com/logrusorgru/aurora"
 )
 
 func main() {
-	r := Red("red")
-	var i int
-	fmt.Printf("%T %p\n", r, Green(&i))
+	var (
+		r = aurora.Red("red")
+		i int
+	)
+	fmt.Printf("%T %p\n", r, aurora.Green(&i))
 }
 ```
 
@@ -315,7 +372,7 @@ The obvious workaround is `Red(fmt.Sprintf("%T", some))`
 
 ### Windows
 
-The Aurora provides ANSI colors only, so there is no support for Windows. That said, there are workarounds available. 
+The Aurora provides ANSI colors only, so there is no support for Windows. That said, there are workarounds available.
 Check out these comments to learn more:
 
 - [Using go-colorable](https://github.com/logrusorgru/aurora/issues/2#issuecomment-299014211).
@@ -329,9 +386,7 @@ on colors for a terminal only, and turn them off for a file.
 
 ### Licensing
 
-Copyright &copy; 2016-2020 The Aurora Authors. This work is free.
+Copyright &copy; 2016-2022 The Aurora Authors. This work is free.
 It comes without any warranty, to the extent permitted by applicable
 law. You can redistribute it and/or modify it under the terms of the
 the Unlicense. See the LICENSE file for more details.
-
-
diff --git a/aurora.go b/aurora.go
index 3b30230..66b644b 100644
--- a/aurora.go
+++ b/aurora.go
@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2016-2020 The Aurora Authors. All rights reserved.
+// Copyright (c) 2016-2022 The Aurora Authors. All rights reserved.
 // This program is free software. It comes without any warranty,
 // to the extent permitted by applicable law. You can redistribute
 // it and/or modify it under the terms of the Unlicense. See LICENSE
@@ -36,690 +36,767 @@
 // Package aurora implements ANSI-colors
 package aurora
 
-import (
-	"fmt"
-)
-
-// An Aurora implements colorizer interface.
-// It also can be a non-colorizer
-type Aurora interface {
-
-	// Reset wraps given argument returning Value
-	// without formats and colors.
-	Reset(arg interface{}) Value
-
-	//
-	// Formats
-	//
-	//
-	// Bold or increased intensity (1).
-	Bold(arg interface{}) Value
-	// Faint, decreased intensity (2).
-	Faint(arg interface{}) Value
-	//
-	// DoublyUnderline or Bold off, double-underline
-	// per ECMA-48 (21).
-	DoublyUnderline(arg interface{}) Value
-	// Fraktur, rarely supported (20).
-	Fraktur(arg interface{}) Value
-	//
-	// Italic, not widely supported, sometimes
-	// treated as inverse (3).
-	Italic(arg interface{}) Value
-	// Underline (4).
-	Underline(arg interface{}) Value
-	//
-	// SlowBlink, blinking less than 150
-	// per minute (5).
-	SlowBlink(arg interface{}) Value
-	// RapidBlink, blinking 150+ per minute,
-	// not widely supported (6).
-	RapidBlink(arg interface{}) Value
-	// Blink is alias for the SlowBlink.
-	Blink(arg interface{}) Value
-	//
-	// Reverse video, swap foreground and
-	// background colors (7).
-	Reverse(arg interface{}) Value
-	// Inverse is alias for the Reverse
-	Inverse(arg interface{}) Value
-	//
-	// Conceal, hidden, not widely supported (8).
-	Conceal(arg interface{}) Value
-	// Hidden is alias for the Conceal
-	Hidden(arg interface{}) Value
-	//
-	// CrossedOut, characters legible, but
-	// marked for deletion (9).
-	CrossedOut(arg interface{}) Value
-	// StrikeThrough is alias for the CrossedOut.
-	StrikeThrough(arg interface{}) Value
-	//
-	// Framed (51).
-	Framed(arg interface{}) Value
-	// Encircled (52).
-	Encircled(arg interface{}) Value
-	//
-	// Overlined (53).
-	Overlined(arg interface{}) Value
-
-	//
-	// Foreground colors
-	//
-	//
-	// Black foreground color (30)
-	Black(arg interface{}) Value
-	// Red foreground color (31)
-	Red(arg interface{}) Value
-	// Green foreground color (32)
-	Green(arg interface{}) Value
-	// Yellow foreground color (33)
-	Yellow(arg interface{}) Value
-	// Brown foreground color (33)
-	//
-	// Deprecated: use Yellow instead, following specification
-	Brown(arg interface{}) Value
-	// Blue foreground color (34)
-	Blue(arg interface{}) Value
-	// Magenta foreground color (35)
-	Magenta(arg interface{}) Value
-	// Cyan foreground color (36)
-	Cyan(arg interface{}) Value
-	// White foreground color (37)
-	White(arg interface{}) Value
-	//
-	// Bright foreground colors
-	//
-	// BrightBlack foreground color (90)
-	BrightBlack(arg interface{}) Value
-	// BrightRed foreground color (91)
-	BrightRed(arg interface{}) Value
-	// BrightGreen foreground color (92)
-	BrightGreen(arg interface{}) Value
-	// BrightYellow foreground color (93)
-	BrightYellow(arg interface{}) Value
-	// BrightBlue foreground color (94)
-	BrightBlue(arg interface{}) Value
-	// BrightMagenta foreground color (95)
-	BrightMagenta(arg interface{}) Value
-	// BrightCyan foreground color (96)
-	BrightCyan(arg interface{}) Value
-	// BrightWhite foreground color (97)
-	BrightWhite(arg interface{}) Value
-	//
-	// Other
-	//
-	// Index of pre-defined 8-bit foreground color
-	// from 0 to 255 (38;5;n).
-	//
-	//       0-  7:  standard colors (as in ESC [ 30–37 m)
-	//       8- 15:  high intensity colors (as in ESC [ 90–97 m)
-	//      16-231:  6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
-	//     232-255:  grayscale from black to white in 24 steps
-	//
-	Index(n uint8, arg interface{}) Value
-	// Gray from 0 to 23.
-	Gray(n uint8, arg interface{}) Value
-
-	//
-	// Background colors
-	//
-	//
-	// BgBlack background color (40)
-	BgBlack(arg interface{}) Value
-	// BgRed background color (41)
-	BgRed(arg interface{}) Value
-	// BgGreen background color (42)
-	BgGreen(arg interface{}) Value
-	// BgYellow background color (43)
-	BgYellow(arg interface{}) Value
-	// BgBrown background color (43)
-	//
-	// Deprecated: use BgYellow instead, following specification
-	BgBrown(arg interface{}) Value
-	// BgBlue background color (44)
-	BgBlue(arg interface{}) Value
-	// BgMagenta background color (45)
-	BgMagenta(arg interface{}) Value
-	// BgCyan background color (46)
-	BgCyan(arg interface{}) Value
-	// BgWhite background color (47)
-	BgWhite(arg interface{}) Value
-	//
-	// Bright background colors
-	//
-	// BgBrightBlack background color (100)
-	BgBrightBlack(arg interface{}) Value
-	// BgBrightRed background color (101)
-	BgBrightRed(arg interface{}) Value
-	// BgBrightGreen background color (102)
-	BgBrightGreen(arg interface{}) Value
-	// BgBrightYellow background color (103)
-	BgBrightYellow(arg interface{}) Value
-	// BgBrightBlue background color (104)
-	BgBrightBlue(arg interface{}) Value
-	// BgBrightMagenta background color (105)
-	BgBrightMagenta(arg interface{}) Value
-	// BgBrightCyan background color (106)
-	BgBrightCyan(arg interface{}) Value
-	// BgBrightWhite background color (107)
-	BgBrightWhite(arg interface{}) Value
-	//
-	// Other
-	//
-	// BgIndex of 8-bit pre-defined background color
-	// from 0 to 255 (48;5;n).
-	//
-	//       0-  7:  standard colors (as in ESC [ 40–47 m)
-	//       8- 15:  high intensity colors (as in ESC [100–107 m)
-	//      16-231:  6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
-	//     232-255:  grayscale from black to white in 24 steps
-	//
-	BgIndex(n uint8, arg interface{}) Value
-	// BgGray from 0 to 23.
-	BgGray(n uint8, arg interface{}) Value
-
-	//
-	// Special
-	//
-	// Colorize removes existing colors and
-	// formats of the argument and applies given.
-	Colorize(arg interface{}, color Color) Value
-
-	//
-	// Support methods
-	//
-	// Sprintf allows to use colored format.
-	Sprintf(format interface{}, args ...interface{}) string
+type Aurora struct {
+	conf Config
+	cc   colorConfig
 }
 
-// NewAurora returns a new Aurora interface that
-// will support or not support colors depending
-// the enableColors argument
-func NewAurora(enableColors bool) Aurora {
-	if enableColors {
-		return aurora{}
-	}
-	return auroraClear{}
-}
-
-// no colors
-
-type auroraClear struct{}
-
-func (auroraClear) Reset(arg interface{}) Value { return valueClear{arg} }
-
-func (auroraClear) Bold(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) Faint(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) DoublyUnderline(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) Fraktur(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) Italic(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) Underline(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) SlowBlink(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) RapidBlink(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) Blink(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) Reverse(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) Inverse(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) Conceal(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) Hidden(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) CrossedOut(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) StrikeThrough(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) Framed(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) Encircled(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) Overlined(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) Black(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) Red(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) Green(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) Yellow(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) Brown(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) Blue(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) Magenta(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) Cyan(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) White(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) BrightBlack(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) BrightRed(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) BrightGreen(arg interface{}) Value {
-	return valueClear{arg}
+// New returns new colorizer by given Options.
+func New(opts ...Option) (a *Aurora) {
+	a = new(Aurora)
+	a.conf = NewConfig()        // set defaults
+	a.conf.Apply(opts...)       // apply options
+	a.cc = a.conf.colorConfig() // keep the short hand
+	return
 }
 
-func (auroraClear) BrightYellow(arg interface{}) Value {
-	return valueClear{arg}
+// Config of the colorizer. It returns copy of the configurations.
+func (a *Aurora) Config() Config {
+	return a.conf
 }
 
-func (auroraClear) BrightBlue(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) BrightMagenta(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) BrightCyan(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) BrightWhite(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) Index(_ uint8, arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) Gray(_ uint8, arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) BgBlack(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) BgRed(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) BgGreen(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) BgYellow(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) BgBrown(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) BgBlue(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) BgMagenta(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) BgCyan(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) BgWhite(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) BgBrightBlack(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) BgBrightRed(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) BgBrightGreen(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) BgBrightYellow(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) BgBrightBlue(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) BgBrightMagenta(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) BgBrightCyan(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) BgBrightWhite(arg interface{}) Value {
-	return valueClear{arg}
-}
-
-func (auroraClear) BgIndex(_ uint8, arg interface{}) Value {
-	return valueClear{arg}
+// Reset wraps given argument returning Value without formats, colors and links.
+func (a *Aurora) Reset(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.Reset()
+	}
+	return Value{
+		cc:    a.cc,
+		value: arg,
+	}
 }
 
-func (auroraClear) BgGray(_ uint8, arg interface{}) Value {
-	return valueClear{arg}
+// Clear wraps given argument returning Value without formats and colors. But
+// preserving links.
+func (a *Aurora) Clear(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.Clear()
+	}
+	return Value{
+		cc:    a.cc,
+		value: arg,
+	}
 }
 
-func (auroraClear) Colorize(arg interface{}, _ Color) Value {
-	return valueClear{arg}
+// Formats
+//
+// Bold or increased intensity (1).
+func (a *Aurora) Bold(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.Bold()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).Bold()),
+		value: arg,
+	}
 }
 
-func (auroraClear) Sprintf(format interface{}, args ...interface{}) string {
-	if str, ok := format.(string); ok {
-		return fmt.Sprintf(str, args...)
+// Faint, decreased intensity (2).
+func (a *Aurora) Faint(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.Faint()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).Faint()),
+		value: arg,
 	}
-	return fmt.Sprintf(fmt.Sprint(format), args...)
 }
 
-// colorized
-
-type aurora struct{}
-
-func (aurora) Reset(arg interface{}) Value {
-	return Reset(arg)
+// DoublyUnderline or Bold off, double-underline per ECMA-48 (21).
+func (a *Aurora) DoublyUnderline(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.DoublyUnderline()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).DoublyUnderline()),
+		value: arg,
+	}
 }
 
-func (aurora) Bold(arg interface{}) Value {
-	return Bold(arg)
+// Fraktur, rarely supported (20).
+func (a *Aurora) Fraktur(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.Fraktur()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).Fraktur()),
+		value: arg,
+	}
 }
 
-func (aurora) Faint(arg interface{}) Value {
-	return Faint(arg)
+// Italic, not widely supported, sometimes treated as inverse (3).
+func (a *Aurora) Italic(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.Italic()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).Italic()),
+		value: arg,
+	}
 }
 
-func (aurora) DoublyUnderline(arg interface{}) Value {
-	return DoublyUnderline(arg)
+// Underline (4).
+func (a *Aurora) Underline(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.Underline()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).Underline()),
+		value: arg,
+	}
 }
 
-func (aurora) Fraktur(arg interface{}) Value {
-	return Fraktur(arg)
+// SlowBlink, blinking less than 150 per minute (5).
+func (a *Aurora) SlowBlink(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.SlowBlink()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).SlowBlink()),
+		value: arg,
+	}
 }
 
-func (aurora) Italic(arg interface{}) Value {
-	return Italic(arg)
+// RapidBlink, blinking 150+ per minute, not widely supported (6).
+func (a *Aurora) RapidBlink(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.RapidBlink()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).RapidBlink()),
+		value: arg,
+	}
 }
 
-func (aurora) Underline(arg interface{}) Value {
-	return Underline(arg)
+// Blink is alias for the SlowBlink.
+func (a *Aurora) Blink(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.Blink()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).Blink()),
+		value: arg,
+	}
 }
 
-func (aurora) SlowBlink(arg interface{}) Value {
-	return SlowBlink(arg)
+// Reverse video, swap foreground and background colors (7).
+func (a *Aurora) Reverse(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.Reverse()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).Reverse()),
+		value: arg,
+	}
 }
 
-func (aurora) RapidBlink(arg interface{}) Value {
-	return RapidBlink(arg)
+// Inverse is alias for the Reverse
+func (a *Aurora) Inverse(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.Inverse()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).Inverse()),
+		value: arg,
+	}
 }
 
-func (aurora) Blink(arg interface{}) Value {
-	return Blink(arg)
+// Conceal, hidden, not widely supported (8).
+func (a *Aurora) Conceal(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.Conceal()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).Conceal()),
+		value: arg,
+	}
 }
 
-func (aurora) Reverse(arg interface{}) Value {
-	return Reverse(arg)
+// Hidden is alias for the Conceal.
+func (a *Aurora) Hidden(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.Hidden()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).Hidden()),
+		value: arg,
+	}
 }
 
-func (aurora) Inverse(arg interface{}) Value {
-	return Inverse(arg)
+// CrossedOut, characters legible, but marked for deletion (9).
+func (a *Aurora) CrossedOut(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.CrossedOut()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).CrossedOut()),
+		value: arg,
+	}
 }
 
-func (aurora) Conceal(arg interface{}) Value {
-	return Conceal(arg)
+// StrikeThrough is alias for the CrossedOut.
+func (a *Aurora) StrikeThrough(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.StrikeThrough()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).StrikeThrough()),
+		value: arg,
+	}
 }
 
-func (aurora) Hidden(arg interface{}) Value {
-	return Hidden(arg)
+// Framed (51).
+func (a *Aurora) Framed(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.Framed()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).Framed()),
+		value: arg,
+	}
 }
 
-func (aurora) CrossedOut(arg interface{}) Value {
-	return CrossedOut(arg)
+// Encircled (52).
+func (a *Aurora) Encircled(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.Encircled()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).Encircled()),
+		value: arg,
+	}
 }
 
-func (aurora) StrikeThrough(arg interface{}) Value {
-	return StrikeThrough(arg)
+// Overlined (53).
+func (a *Aurora) Overlined(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.Overlined()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).Overlined()),
+		value: arg,
+	}
 }
 
-func (aurora) Framed(arg interface{}) Value {
-	return Framed(arg)
+// Foreground colors
+//
+// Black foreground color (30).
+func (a *Aurora) Black(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.Black()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).Black()),
+		value: arg,
+	}
 }
 
-func (aurora) Encircled(arg interface{}) Value {
-	return Encircled(arg)
+// Red foreground color (31).
+func (a *Aurora) Red(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.Red()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).Red()),
+		value: arg,
+	}
 }
 
-func (aurora) Overlined(arg interface{}) Value {
-	return Overlined(arg)
+// Green foreground color (32).
+func (a *Aurora) Green(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.Green()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).Green()),
+		value: arg,
+	}
 }
 
-func (aurora) Black(arg interface{}) Value {
-	return Black(arg)
+// Yellow foreground color (33).
+func (a *Aurora) Yellow(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.Yellow()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).Yellow()),
+		value: arg,
+	}
 }
 
-func (aurora) Red(arg interface{}) Value {
-	return Red(arg)
+// Blue foreground color (34).
+func (a *Aurora) Blue(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.Blue()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).Blue()),
+		value: arg,
+	}
 }
 
-func (aurora) Green(arg interface{}) Value {
-	return Green(arg)
+// Magenta foreground color (35).
+func (a *Aurora) Magenta(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.Magenta()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).Magenta()),
+		value: arg,
+	}
 }
 
-func (aurora) Yellow(arg interface{}) Value {
-	return Yellow(arg)
+// Cyan foreground color (36).
+func (a *Aurora) Cyan(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.Cyan()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).Cyan()),
+		value: arg,
+	}
 }
 
-func (aurora) Brown(arg interface{}) Value {
-	return Brown(arg)
+// White foreground color (37).
+func (a *Aurora) White(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.White()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).White()),
+		value: arg,
+	}
 }
 
-func (aurora) Blue(arg interface{}) Value {
-	return Blue(arg)
+// Bright foreground colors.
+//
+// BrightBlack foreground color (90).
+func (a *Aurora) BrightBlack(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.BrightBlack()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).BrightBlack()),
+		value: arg,
+	}
 }
 
-func (aurora) Magenta(arg interface{}) Value {
-	return Magenta(arg)
+// BrightRed foreground color (91).
+func (a *Aurora) BrightRed(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.BrightRed()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).BrightRed()),
+		value: arg,
+	}
 }
 
-func (aurora) Cyan(arg interface{}) Value {
-	return Cyan(arg)
+// BrightGreen foreground color (92).
+func (a *Aurora) BrightGreen(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.BrightGreen()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).BrightGreen()),
+		value: arg,
+	}
 }
 
-func (aurora) White(arg interface{}) Value {
-	return White(arg)
+// BrightYellow foreground color (93).
+func (a *Aurora) BrightYellow(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.BrightYellow()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).BrightYellow()),
+		value: arg,
+	}
 }
 
-func (aurora) BrightBlack(arg interface{}) Value {
-	return BrightBlack(arg)
+// BrightBlue foreground color (94).
+func (a *Aurora) BrightBlue(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.BrightBlue()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).BrightBlue()),
+		value: arg,
+	}
 }
 
-func (aurora) BrightRed(arg interface{}) Value {
-	return BrightRed(arg)
+// BrightMagenta foreground color (95).
+func (a *Aurora) BrightMagenta(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.BrightMagenta()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).BrightMagenta()),
+		value: arg,
+	}
 }
 
-func (aurora) BrightGreen(arg interface{}) Value {
-	return BrightGreen(arg)
+// BrightCyan foreground color (96).
+func (a *Aurora) BrightCyan(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.BrightCyan()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).BrightCyan()),
+		value: arg,
+	}
 }
 
-func (aurora) BrightYellow(arg interface{}) Value {
-	return BrightYellow(arg)
+// BrightWhite foreground color (97).
+func (a *Aurora) BrightWhite(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.BrightWhite()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).BrightWhite()),
+		value: arg,
+	}
 }
 
-func (aurora) BrightBlue(arg interface{}) Value {
-	return BrightBlue(arg)
+// Other colors.
+//
+// Index of pre-defined 8-bit foreground color from 0 to 255 (38;5;n).
+//
+//	  0-  7:  standard colors (as in ESC [ 30–37 m)
+//	  8- 15:  high intensity colors (as in ESC [ 90–97 m)
+//	 16-231:  6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
+//	232-255:  grayscale from black to white in 24 steps
+func (a *Aurora) Index(n ColorIndex, arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.Index(n)
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).Index(n)),
+		value: arg,
+	}
 }
 
-func (aurora) BrightMagenta(arg interface{}) Value {
-	return BrightMagenta(arg)
+// Gray from 0 to 23.
+func (a *Aurora) Gray(n GrayIndex, arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.Gray(n)
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).Gray(n)),
+		value: arg,
+	}
 }
 
-func (aurora) BrightCyan(arg interface{}) Value {
-	return BrightCyan(arg)
+// Background colors.
+//
+// BgBlack background color (40).
+func (a *Aurora) BgBlack(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.BgBlack()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).BgBlack()),
+		value: arg,
+	}
 }
 
-func (aurora) BrightWhite(arg interface{}) Value {
-	return BrightWhite(arg)
+// BgRed background color (41).
+func (a *Aurora) BgRed(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.BgRed()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).BgRed()),
+		value: arg,
+	}
 }
 
-func (aurora) Index(index uint8, arg interface{}) Value {
-	return Index(index, arg)
+// BgGreen background color (42).
+func (a *Aurora) BgGreen(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.BgGreen()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).BgGreen()),
+		value: arg,
+	}
 }
 
-func (aurora) Gray(n uint8, arg interface{}) Value {
-	return Gray(n, arg)
+// BgYellow background color (43).
+func (a *Aurora) BgYellow(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.BgYellow()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).BgYellow()),
+		value: arg,
+	}
 }
 
-func (aurora) BgBlack(arg interface{}) Value {
-	return BgBlack(arg)
+// BgBlue background color (44).
+func (a *Aurora) BgBlue(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.BgBlue()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).BgBlue()),
+		value: arg,
+	}
 }
 
-func (aurora) BgRed(arg interface{}) Value {
-	return BgRed(arg)
+// BgMagenta background color (45).
+func (a *Aurora) BgMagenta(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.BgMagenta()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).BgMagenta()),
+		value: arg,
+	}
 }
 
-func (aurora) BgGreen(arg interface{}) Value {
-	return BgGreen(arg)
+// BgCyan background color (46).
+func (a *Aurora) BgCyan(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.BgCyan()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).BgCyan()),
+		value: arg,
+	}
 }
 
-func (aurora) BgYellow(arg interface{}) Value {
-	return BgYellow(arg)
+// BgWhite background color (47).
+func (a *Aurora) BgWhite(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.BgWhite()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).BgWhite()),
+		value: arg,
+	}
 }
 
-func (aurora) BgBrown(arg interface{}) Value {
-	return BgBrown(arg)
+// Bright background colors.
+//
+// BgBrightBlack background color (100).
+func (a *Aurora) BgBrightBlack(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.BgBrightBlack()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).BgBrightBlack()),
+		value: arg,
+	}
 }
 
-func (aurora) BgBlue(arg interface{}) Value {
-	return BgBlue(arg)
+// BgBrightRed background color (101).
+func (a *Aurora) BgBrightRed(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.BgBrightRed()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).BgBrightRed()),
+		value: arg,
+	}
 }
 
-func (aurora) BgMagenta(arg interface{}) Value {
-	return BgMagenta(arg)
+// BgBrightGreen background color (102).
+func (a *Aurora) BgBrightGreen(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.BgBrightGreen()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).BgBrightGreen()),
+		value: arg,
+	}
 }
 
-func (aurora) BgCyan(arg interface{}) Value {
-	return BgCyan(arg)
+// BgBrightYellow background color (103).
+func (a *Aurora) BgBrightYellow(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.BgBrightYellow()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).BgBrightYellow()),
+		value: arg,
+	}
 }
 
-func (aurora) BgWhite(arg interface{}) Value {
-	return BgWhite(arg)
+// BgBrightBlue background color (104).
+func (a *Aurora) BgBrightBlue(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.BgBrightBlue()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).BgBrightBlue()),
+		value: arg,
+	}
 }
 
-func (aurora) BgBrightBlack(arg interface{}) Value {
-	return BgBrightBlack(arg)
+// BgBrightMagenta background color (105).
+func (a *Aurora) BgBrightMagenta(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.BgBrightMagenta()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).BgBrightMagenta()),
+		value: arg,
+	}
 }
 
-func (aurora) BgBrightRed(arg interface{}) Value {
-	return BgBrightRed(arg)
+// BgBrightCyan background color (106).
+func (a *Aurora) BgBrightCyan(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.BgBrightCyan()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).BgBrightCyan()),
+		value: arg,
+	}
 }
 
-func (aurora) BgBrightGreen(arg interface{}) Value {
-	return BgBrightGreen(arg)
+// BgBrightWhite background color (107).
+func (a *Aurora) BgBrightWhite(arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.BgBrightWhite()
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).BgBrightWhite()),
+		value: arg,
+	}
 }
 
-func (aurora) BgBrightYellow(arg interface{}) Value {
-	return BgBrightYellow(arg)
+// Other background colors.
+//
+// BgIndex of 8-bit pre-defined background color from 0 to 255 (48;5;n).
+//
+//	  0-  7:  standard colors (as in ESC [ 40–47 m)
+//	  8- 15:  high intensity colors (as in ESC [100–107 m)
+//	 16-231:  6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
+//	232-255:  grayscale from black to white in 24 steps
+func (a *Aurora) BgIndex(n ColorIndex, arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.BgIndex(n)
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).BgIndex(n)),
+		value: arg,
+	}
 }
 
-func (aurora) BgBrightBlue(arg interface{}) Value {
-	return BgBrightBlue(arg)
+// BgGray from 0 to 23.
+func (a *Aurora) BgGray(n GrayIndex, arg interface{}) Value {
+	if val, ok := arg.(Value); ok {
+		return val.BgGray(n)
+	}
+	return Value{
+		cc:    a.cc | colorConfig(Color(0).BgGray(n)),
+		value: arg,
+	}
 }
 
-func (aurora) BgBrightMagenta(arg interface{}) Value {
-	return BgBrightMagenta(arg)
+// Special color functions.
+//
+// Colorize removes existing colors and
+// formats of the argument and applies given.
+func (a *Aurora) Colorize(arg interface{}, color Color) Value {
+	if val, ok := arg.(Value); ok {
+		return val.Colorize(color)
+	}
+	return Value{
+		cc:    a.cc | colorConfig(color),
+		value: arg,
+	}
 }
 
-func (aurora) BgBrightCyan(arg interface{}) Value {
-	return BgBrightCyan(arg)
-}
+// Hyperlinks feature
+//
+// Hyperlink with given target and parameters. If hyperlinks feature is
+// disabled, then the 'arg' argument dropped and the 'target' used instead
+// inheriting all colors and format from the 'arg' (if it's a Colored).
+//
+// See https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
+// for details about the hyperlinks feature.
+//
+// The Hyperlink doesn't escape the target and the params. They should be
+// checked and escaped before.
+//
+// See also HyperlinkID function.
+//
+// For a simple example
+//
+//	au.Hyperlink("Example", "http://example.com")
+//
+// and an example with ID
+//
+//	au.Hyperlink("Example", "http://example.com", aurora.HyperlinkID("10"))
+func (a *Aurora) Hyperlink(arg interface{}, target string,
+	params ...HyperlinkParam) Value {
 
-func (aurora) BgBrightWhite(arg interface{}) Value {
-	return BgBrightWhite(arg)
+	if val, ok := arg.(Value); ok {
+		return val.Hyperlink(target, params...)
+	}
+	return Value{
+		cc:    a.cc,
+		value: arg,
+	}.Hyperlink(target, params...)
 }
 
-func (aurora) BgIndex(n uint8, arg interface{}) Value {
-	return BgIndex(n, arg)
+// HyperlinkTarget of the argument if it's a Value.
+func (a *Aurora) HyperlinkTarget(arg interface{}) (target string) {
+	if val, ok := arg.(Value); ok {
+		return val.HyperlinkTarget()
+	}
+	return // no target
 }
 
-func (aurora) BgGray(n uint8, arg interface{}) Value {
-	return BgGray(n, arg)
+// HyperlinkParams of the argument if it's a Value.
+func (a *Aurora) HyperlinkParams(arg interface{}) (params []HyperlinkParam) {
+	if val, ok := arg.(Value); ok {
+		return val.HyperlinkParams()
+	}
+	return // no target
 }
 
-func (aurora) Colorize(arg interface{}, color Color) Value {
-	return Colorize(arg, color)
+func (a *Aurora) transform(arg interface{}) (val Value, ok bool) {
+	var ai Value
+	ai, ok = arg.(Value)
+	if !ok {
+		return // Value{}, false
+	}
+	// if ai.cc.resetColor() == a.cc.resetColor() {
+	// 	return // don't replace, same configurations
+	// }
+	val = Value{cc: a.cc | colorConfig(ai.cc.color()), value: ai.value}
+	if a.cc.hyperlinksEnbaled() {
+		val.hyperlink = ai.hyperlink
+	}
+	return val, true // transformed value, true
 }
 
-func (aurora) Sprintf(format interface{}, args ...interface{}) string {
-	return Sprintf(format, args...)
+// Sprintf allows to use Value as format. For example
+//
+//	var v = Sprintf(Red("total: +3.5f points"), Blue(3.14))
+//
+// In this case "total:" and "points" will be red, but
+// 3.14 will be blue. But, in another example
+//
+//	var v = Sprintf(Red("total: +3.5f points"), 3.14)
+//
+// full string will be red. And no way to clear 3.14 to default format and
+// color.
+//
+// It applies own configurations to all given Values.
+func (a *Aurora) Sprintf(format interface{}, args ...interface{}) string {
+	// // clear colors & links as configured by the a
+	if f, ok := a.transform(format); ok {
+		format = f
+	}
+	for i := range args {
+		if ax, ok := a.transform(args[i]); ok {
+			args[i] = ax
+		}
+	}
+	return sprintf(format, args...)
 }
diff --git a/aurora_hyperlinks.gif b/aurora_hyperlinks.gif
new file mode 100644
index 0000000..03054de
Binary files /dev/null and b/aurora_hyperlinks.gif differ
diff --git a/aurora_hyperlinks.webm b/aurora_hyperlinks.webm
new file mode 100644
index 0000000..1ec190b
Binary files /dev/null and b/aurora_hyperlinks.webm differ
diff --git a/aurora_hyperlinks_flags.png b/aurora_hyperlinks_flags.png
new file mode 100644
index 0000000..a1c6f40
Binary files /dev/null and b/aurora_hyperlinks_flags.png differ
diff --git a/aurora_test.go b/aurora_test.go
index 158e639..8a841eb 100644
--- a/aurora_test.go
+++ b/aurora_test.go
@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2016-2020 The Aurora Authors. All rights reserved.
+// Copyright (c) 2016-2022 The Aurora Authors. All rights reserved.
 // This program is free software. It comes without any warranty,
 // to the extent permitted by applicable law. You can redistribute
 // it and/or modify it under the terms of the Unlicense. See LICENSE
@@ -37,45 +37,48 @@ package aurora
 
 import (
 	"testing"
-)
 
-func isClear(v Value) bool {
-	return v.Color() == 0 && v.tail() == 0
-}
+	"github.com/stretchr/testify/assert"
+)
 
 func isColor(v Value, clr Color) bool {
 	return v.Color() == clr
 }
 
-func isTail(v Value, tl Color) bool {
-	return v.tail() == tl
+func Test_New(t *testing.T) {
+	var (
+		dconf = NewConfig() // default configurations
+		a     = New()
+	)
+	assert.Equal(t, dconf, a.conf, "non-default configurations")
+	// options
+	a = New(WithColors(true), WithHyperlinks(true))
+	assert.True(t, a.conf.Colors)
+	assert.True(t, a.conf.Hyperlinks)
+	// colors
+	a = New(WithColors(false), WithHyperlinks(false))
+	assert.False(t, a.conf.Colors)
+	assert.False(t, a.conf.Hyperlinks)
+
 }
 
-func Test_NewAurora(t *testing.T) {
-	if a := NewAurora(false); a == nil {
-		t.Error("NewAurora(false) returns nil")
-	}
-	if a := NewAurora(true); a == nil {
-		t.Error("NewAurora(true) returns nil")
-	}
-	if t.Failed() {
-		t.FailNow()
-	}
+func TestAurora_Config(t *testing.T) {
+	assert.Equal(t, NewConfig(), New().Config())
 }
 
-func Test_auroraClear_methods(t *testing.T) {
-	a := NewAurora(false)
-	test := func(mn string, v Value) {
-		if !isClear(v) {
-			t.Errorf("NewAurora(false).%s is not clear", mn)
-		} else if str, ok := v.Value().(string); !ok {
-			t.Errorf("NewAurora(false).%s wrong value type", mn)
-		} else if str != "x" {
-			t.Errorf("NewAurora(false).%s wrong value", mn)
-		}
+func TestAurora_no_colors(t *testing.T) {
+
+	var a = New(WithColors(false), WithHyperlinks(false))
+
+	var test = func(mn string, v Value) {
+		t.Helper()
+		t.Log(mn)
+		assert.Zero(t, v.Color(), "colored")
+		assert.Equal(t, "x", v.Value(), "wrong value")
 	}
 
 	test("Reset", a.Reset("x"))
+	test("Clear", a.Clear("x"))
 
 	test("Bold", a.Bold("x"))
 	test("Faint", a.Faint("x"))
@@ -100,7 +103,6 @@ func Test_auroraClear_methods(t *testing.T) {
 	test("Red", a.Red("x"))
 	test("Green", a.Green("x"))
 	test("Yellow", a.Yellow("x"))
-	test("Brown", a.Brown("x"))
 	test("Blue", a.Blue("x"))
 	test("Magenta", a.Magenta("x"))
 	test("Cyan", a.Cyan("x"))
@@ -120,7 +122,6 @@ func Test_auroraClear_methods(t *testing.T) {
 	test("BgRed", a.BgRed("x"))
 	test("BgGreen", a.BgGreen("x"))
 	test("BgYellow", a.BgYellow("x"))
-	test("BgBrown", a.BgBrown("x"))
 	test("BgBlue", a.BgBlue("x"))
 	test("BgMagenta", a.BgMagenta("x"))
 	test("BgCyan", a.BgCyan("x"))
@@ -140,29 +141,21 @@ func Test_auroraClear_methods(t *testing.T) {
 
 }
 
-func Test_auroraClear_sprintf(t *testing.T) {
-	a := NewAurora(false)
-	if s := a.Sprintf(a.Black("x: %d"), a.Blue(2)); s != "x: 2" {
-		t.Error("NewAurora(false).Sprintf wrong value")
-	}
-	if s := a.Sprintf("x: %d", a.Blue(2)); s != "x: 2" {
-		t.Error("NewAurora(false).Sprintf wrong value")
-	}
+func Test_noColors_sprintf(t *testing.T) {
+	var au = New(WithColors(false))
+	assert.Equal(t, "x: 2", au.Sprintf(au.Black("x: %d"), au.Blue(2)))
+	assert.Equal(t, "x: 2", au.Sprintf("x: %d", au.Blue(2)))
 }
 
-func Test_aurora_methods(t *testing.T) {
-	a := NewAurora(true)
-	test := func(mn string, v Value, clr Color) {
+func TestAurora_colored(t *testing.T) {
+
+	var a = New() // with colors, with hyperlinks
+
+	var test = func(mn string, v Value, clr Color) {
 		t.Helper()
-		if !isColor(v, clr) {
-			t.Errorf("NewAurora(true).%s wrong color: %d", mn, v.Color())
-		} else if !isTail(v, 0) {
-			t.Errorf("NewAurora(true).%s unexpected tail value", mn)
-		} else if str, ok := v.Value().(string); !ok {
-			t.Errorf("NewAurora(true).%s wrong value type", mn)
-		} else if str != "x" {
-			t.Errorf("NewAurora(true).%s wrong value", mn)
-		}
+		t.Log(mn)
+		assert.Equal(t, clr, v.Color())
+		assert.Equal(t, "x", v.Value(), "wrong value")
 	}
 	test("Reset", a.Reset("x"), 0)
 
@@ -189,7 +182,6 @@ func Test_aurora_methods(t *testing.T) {
 	test("Red", a.Red("x"), RedFg)
 	test("Green", a.Green("x"), GreenFg)
 	test("Yellow", a.Yellow("x"), YellowFg)
-	test("Brown", a.Brown("x"), BrownFg)
 	test("Blue", a.Blue("x"), BlueFg)
 	test("Magenta", a.Magenta("x"), MagentaFg)
 	test("Cyan", a.Cyan("x"), CyanFg)
@@ -209,7 +201,6 @@ func Test_aurora_methods(t *testing.T) {
 	test("BgRed", a.BgRed("x"), RedBg)
 	test("BgGreen", a.BgGreen("x"), GreenBg)
 	test("BgYellow", a.BgYellow("x"), YellowBg)
-	test("BgBrown", a.BgBrown("x"), BrownBg)
 	test("BgBlue", a.BgBlue("x"), BlueBg)
 	test("BgMagenta", a.BgMagenta("x"), MagentaBg)
 	test("BgCyan", a.BgCyan("x"), CyanBg)
@@ -229,14 +220,10 @@ func Test_aurora_methods(t *testing.T) {
 		RedFg|BlueBg|BrightBg|BoldFm)
 }
 
-func Test_aurora_Sprintf(t *testing.T) {
-	a := NewAurora(true)
-	s := a.Sprintf(a.Black("x: %dB"), a.Blue(2))
-	if s != "\033[30mx: \033[0;34m2\033[0;30mB\033[0m" {
-		t.Errorf("NewAurora(true).Sprintf wrong value: %q", s)
-	}
-	s = a.Sprintf("x: %dB", a.Blue(2))
-	if s != "x: \033[34m2\033[0mB" {
-		t.Errorf("NewAurora(true).Sprintf wrong value: %q", s)
-	}
+func TestAurora_Sprintf(t *testing.T) {
+	var a = New()
+	assert.Equal(t, "\033[30mx: \033[0;34m2\033[0;30mB\033[0m",
+		a.Sprintf(a.Black("x: %dB"), a.Blue(2)))
+	assert.Equal(t, "x: \033[34m2\033[0mB",
+		a.Sprintf("x: %dB", a.Blue(2)))
 }
diff --git a/bench_test.go b/bench_test.go
index e30cb9a..1cc60c0 100644
--- a/bench_test.go
+++ b/bench_test.go
@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2016-2020 The Aurora Authors. All rights reserved.
+// Copyright (c) 2016-2022 The Aurora Authors. All rights reserved.
 // This program is free software. It comes without any warranty,
 // to the extent permitted by applicable law. You can redistribute
 // it and/or modify it under the terms of the Unlicense. See LICENSE
@@ -54,7 +54,7 @@ var (
 )
 
 // 18 values
-func simpleValues(a Aurora, x string) []Value {
+func simpleValues(a *Aurora, x string) []Value {
 	return []Value{
 		a.Reset("x"),
 
@@ -81,7 +81,7 @@ func simpleValues(a Aurora, x string) []Value {
 		a.Red("x"),
 		a.Green("x"),
 		a.Yellow("x"),
-		a.Brown("x"),
+		a.Yellow("x"),
 		a.Blue("x"),
 		a.Magenta("x"),
 		a.Cyan("x"),
@@ -101,7 +101,7 @@ func simpleValues(a Aurora, x string) []Value {
 		a.BgRed("x"),
 		a.BgGreen("x"),
 		a.BgYellow("x"),
-		a.BgBrown("x"),
+		a.BgYellow("x"),
 		a.BgBlue("x"),
 		a.BgMagenta("x"),
 		a.BgCyan("x"),
@@ -120,7 +120,7 @@ func simpleValues(a Aurora, x string) []Value {
 }
 
 // 18 values
-func complexValues(a Aurora, x string) []Value {
+func complexValues(a *Aurora, x string) []Value {
 
 	var allFormats = func(val Value) Value {
 		return val.Bold().DoublyUnderline().Fraktur().Blink().Italic().
@@ -155,7 +155,7 @@ func complexValues(a Aurora, x string) []Value {
 		allFormats(a.Red(x).BgBrightRed()),
 		allFormats(a.Green(x).BgBrightRed()),
 		allFormats(a.Yellow(x).BgBrightRed()),
-		allFormats(a.Brown(x).BgBrightRed()),
+		allFormats(a.Yellow(x).BgBrightRed()),
 		allFormats(a.Blue(x).BgBrightRed()),
 		allFormats(a.Magenta(x).BgBrightRed()),
 		allFormats(a.Cyan(x).BgBrightRed()),
@@ -175,7 +175,7 @@ func complexValues(a Aurora, x string) []Value {
 		allFormats(a.BgRed(x).BrightRed()),
 		allFormats(a.BgGreen(x).BrightGreen()),
 		allFormats(a.BgYellow(x).BrightYellow()),
-		allFormats(a.BgBrown(x).BrightYellow()),
+		allFormats(a.BgYellow(x).BrightYellow()),
 		allFormats(a.BgBlue(x).BrightBlue()),
 		allFormats(a.BgMagenta(x).BrightMagenta()),
 		allFormats(a.BgCyan(x).BrightCyan()),
@@ -193,7 +193,7 @@ func complexValues(a Aurora, x string) []Value {
 	}
 }
 
-func benchSimpleValue(b *testing.B, a Aurora, x string) {
+func benchSimpleValue(b *testing.B, a *Aurora, x string) {
 	b.ResetTimer()
 	for i := 0; i < b.N; i++ {
 		gVals = simpleValues(a, x)
@@ -201,7 +201,7 @@ func benchSimpleValue(b *testing.B, a Aurora, x string) {
 	b.ReportAllocs()
 }
 
-func benchComplexValue(b *testing.B, a Aurora, x string) {
+func benchComplexValue(b *testing.B, a *Aurora, x string) {
 	b.ResetTimer()
 	for i := 0; i < b.N; i++ {
 		gVals = complexValues(a, x)
@@ -212,14 +212,14 @@ func benchComplexValue(b *testing.B, a Aurora, x string) {
 func benchValueString(b *testing.B, vals []Value) {
 	b.ResetTimer()
 	for i := 0; i < b.N; i++ {
-		for j := 0; j < len(vals); j++ { // TODO: range and allocs ?
+		for j := 0; j < len(vals); j++ {
 			gStr = vals[j].String()
 		}
 	}
 	b.ReportAllocs()
 }
 
-func benchSprintf(b *testing.B, a Aurora, format interface{},
+func benchSprintf(b *testing.B, a *Aurora, format interface{},
 	args ...interface{}) {
 	b.ResetTimer()
 	for i := 0; i < b.N; i++ {
@@ -236,7 +236,7 @@ func toInterfaces(vals []Value) []interface{} {
 	return r
 }
 
-func auroraBench(a Aurora, b *testing.B) {
+func auroraBench(a *Aurora, b *testing.B) {
 	// a.Red("...")
 	b.Run("simple value", func(b *testing.B) {
 		b.Run("short", func(b *testing.B) { benchSimpleValue(b, a, short) })
@@ -377,13 +377,13 @@ func auroraBench(a Aurora, b *testing.B) {
 }
 
 func Benchmark_auroraClear(b *testing.B) {
-	a := NewAurora(false)
+	var a = New(WithColors(false), WithHyperlinks(false))
 	auroraBench(a, b)
 }
 
 // create a value
 func Benchmark_aurora(b *testing.B) {
-	a := NewAurora(true)
+	var a = New()
 	auroraBench(a, b)
 }
 
@@ -419,7 +419,7 @@ func Benchmark_wrap(b *testing.B) {
 		{"Red", Red},
 		{"Green", Green},
 		{"Yellow", Yellow},
-		{"Brown", Brown},
+		{"Yellow", Yellow},
 		{"Blue", Blue},
 		{"Magenta", Magenta},
 		{"Cyan", Cyan},
@@ -437,7 +437,7 @@ func Benchmark_wrap(b *testing.B) {
 		{"BgRed", BgRed},
 		{"BgGreen", BgGreen},
 		{"BgYellow", BgYellow},
-		{"BgBrown", BgBrown},
+		{"BgYellow", BgYellow},
 		{"BgBlue", BgBlue},
 		{"BgMagenta", BgMagenta},
 		{"BgCyan", BgCyan},
diff --git a/color.go b/color.go
index 486fb67..d699159 100644
--- a/color.go
+++ b/color.go
@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2016-2020 The Aurora Authors. All rights reserved.
+// Copyright (c) 2016-2022 The Aurora Authors. All rights reserved.
 // This program is free software. It comes without any warranty,
 // to the extent permitted by applicable law. You can redistribute
 // it and/or modify it under the terms of the Unlicense. See LICENSE
@@ -45,7 +45,7 @@ type Color uint
 
 	Developer note.
 
-	The int type is architecture depended and can be
+	The uint type is architecture depended and can be
 	represented as int32 or int64.
 
 	Thus, we can use 32-bits only to be fast and
@@ -112,7 +112,7 @@ const (
 	// 8 bits
 
 	// [  0;   7] - 30-37
-	// [  8;  15] - 90-97
+	// [  8;  15] - 90-97 bright
 	// [ 16; 231] - RGB
 	// [232; 255] - grayscale
 
@@ -132,11 +132,6 @@ const (
 
 	// 5 bits
 
-	// BrownFg represents brown foreground color.
-	//
-	// Deprecated: use YellowFg instead, following specifications
-	BrownFg = YellowFg
-
 	//
 	maskFg = (0xff << shiftFg) | flagFg
 )
@@ -147,7 +142,7 @@ const (
 	// 8 bits
 
 	// [  0;   7] - 40-47
-	// [  8;  15] - 100-107
+	// [  8;  15] - 100-107 bright
 	// [ 16; 231] - RGB
 	// [232; 255] - grayscale
 
@@ -167,11 +162,6 @@ const (
 
 	// 5 bits
 
-	// BrownBg represents brown foreground color.
-	//
-	// Deprecated: use YellowBg instead, following specifications
-	BrownBg = YellowBg
-
 	//
 	maskBg = (0xff << shiftBg) | flagBg
 )
@@ -182,13 +172,6 @@ const (
 	clear      = esc + "0m"
 )
 
-// IsValid returns true always
-//
-// Deprecated: don't use this method anymore
-func (c Color) IsValid() bool {
-	return true
-}
-
 // Nos returns string like 1;7;31;45. It
 // may be an empty string for empty color.
 // If the zero is true, then the string
@@ -396,3 +379,335 @@ func (c Color) appendNos(bs []byte, zero bool) []byte {
 
 	return bs
 }
+
+// ColorIndex is index of pre-defined 8-bit foreground or
+// background colors from 0 to 255 (38;5;n).
+//
+//	  0-  7:  standard colors (as in ESC [ 30–37 m)
+//	  8- 15:  high intensity colors (as in ESC [ 90–97 m)
+//	 16-231:  6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
+//	232-255:  grayscale from black to white in 24 steps
+type ColorIndex uint8
+
+// GrayIndex from 0 to 23.
+type GrayIndex uint8
+
+// The Colored interface represents a value with a Color.
+type Colored interface {
+	Color() Color // color of the value
+}
+
+// Reset returns Color without a color and formats.
+func (c Color) Reset() Color {
+	return Color(0)
+}
+
+//
+// Formats
+//
+
+// Bold or increased intensity (1).
+func (c Color) Bold() Color {
+	return (c &^ FaintFm) | BoldFm
+}
+
+// Faint, decreased intensity (2).
+func (c Color) Faint() Color {
+	return (c &^ BoldFm) | FaintFm
+}
+
+// DoublyUnderline or Bold off, double-underline
+// per ECMA-48 (21).
+func (c Color) DoublyUnderline() Color {
+	return (c &^ UnderlineFm) | DoublyUnderlineFm
+}
+
+// Fraktur, rarely supported (20).
+func (c Color) Fraktur() Color {
+	return c | FrakturFm
+}
+
+// Italic, not widely supported, sometimes
+// treated as inverse (3).
+func (c Color) Italic() Color {
+	return c | ItalicFm
+}
+
+// Underline (4).
+func (c Color) Underline() Color {
+	return (c &^ DoublyUnderlineFm) | UnderlineFm
+}
+
+// SlowBlink, blinking less than 150
+// per minute (5).
+func (c Color) SlowBlink() Color {
+	return (c &^ RapidBlinkFm) | SlowBlinkFm
+}
+
+// RapidBlink, blinking 150+ per minute,
+// not widely supported (6).
+func (c Color) RapidBlink() Color {
+	return (c &^ SlowBlinkFm) | RapidBlinkFm
+}
+
+// Blink is alias for the SlowBlink.
+func (c Color) Blink() Color {
+	return c.SlowBlink()
+}
+
+// Reverse video, swap foreground and
+// background colors (7).
+func (c Color) Reverse() Color {
+	return c | ReverseFm
+}
+
+// Inverse is alias for the Reverse
+func (c Color) Inverse() Color {
+	return c.Reverse()
+}
+
+// Conceal, hidden, not widely supported (8).
+func (c Color) Conceal() Color {
+	return c | ConcealFm
+}
+
+// Hidden is alias for the Conceal
+func (c Color) Hidden() Color {
+	return c.Conceal()
+}
+
+// CrossedOut, characters legible, but
+// marked for deletion (9).
+func (c Color) CrossedOut() Color {
+	return c | CrossedOutFm
+}
+
+// StrikeThrough is alias for the CrossedOut.
+func (c Color) StrikeThrough() Color {
+	return c.CrossedOut()
+}
+
+// Framed (51).
+func (c Color) Framed() Color {
+	return c | FramedFm
+}
+
+// Encircled (52).
+func (c Color) Encircled() Color {
+	return c | EncircledFm
+}
+
+// Overlined (53).
+func (c Color) Overlined() Color {
+	return c | OverlinedFm
+}
+
+// Foreground colors
+//
+// Black foreground color (30)
+func (c Color) Black() Color {
+	return (c &^ maskFg) | BlackFg
+}
+
+// Red foreground color (31)
+func (c Color) Red() Color {
+	return (c &^ maskFg) | RedFg
+}
+
+// Green foreground color (32)
+func (c Color) Green() Color {
+	return (c &^ maskFg) | GreenFg
+}
+
+// Yellow foreground color (33)
+func (c Color) Yellow() Color {
+	return (c &^ maskFg) | YellowFg
+}
+
+// Blue foreground color (34)
+func (c Color) Blue() Color {
+	return (c &^ maskFg) | BlueFg
+}
+
+// Magenta foreground color (35)
+func (c Color) Magenta() Color {
+	return (c &^ maskFg) | MagentaFg
+}
+
+// Cyan foreground color (36)
+func (c Color) Cyan() Color {
+	return (c &^ maskFg) | CyanFg
+}
+
+// White foreground color (37)
+func (c Color) White() Color {
+	return (c &^ maskFg) | WhiteFg
+}
+
+// Bright foreground colors
+//
+// BrightBlack foreground color (90)
+func (c Color) BrightBlack() Color {
+	return (c &^ maskFg) | BrightFg | BlackFg
+}
+
+// BrightRed foreground color (91)
+func (c Color) BrightRed() Color {
+	return (c &^ maskFg) | BrightFg | RedFg
+}
+
+// BrightGreen foreground color (92)
+func (c Color) BrightGreen() Color {
+	return (c &^ maskFg) | BrightFg | GreenFg
+}
+
+// BrightYellow foreground color (93)
+func (c Color) BrightYellow() Color {
+	return (c &^ maskFg) | BrightFg | YellowFg
+}
+
+// BrightBlue foreground color (94)
+func (c Color) BrightBlue() Color {
+	return (c &^ maskFg) | BrightFg | BlueFg
+}
+
+// BrightMagenta foreground color (95)
+func (c Color) BrightMagenta() Color {
+	return (c &^ maskFg) | BrightFg | MagentaFg
+}
+
+// BrightCyan foreground color (96)
+func (c Color) BrightCyan() Color {
+	return (c &^ maskFg) | BrightFg | CyanFg
+}
+
+// BrightWhite foreground color (97)
+func (c Color) BrightWhite() Color {
+	return (c &^ maskFg) | BrightFg | WhiteFg
+}
+
+// Other
+//
+// Index of pre-defined 8-bit foreground color
+// from 0 to 255 (38;5;n).
+//
+//	  0-  7:  standard colors (as in ESC [ 30–37 m)
+//	  8- 15:  high intensity colors (as in ESC [ 90–97 m)
+//	 16-231:  6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
+//	232-255:  grayscale from black to white in 24 steps
+func (c Color) Index(ci ColorIndex) Color {
+	return (c &^ maskFg) | (Color(ci) << shiftFg) | flagFg
+}
+
+// Gray from 0 to 23.
+func (c Color) Gray(n GrayIndex) Color {
+	if n > 23 {
+		n = 23
+	}
+	return (c &^ maskFg) | (Color(232+n) << shiftFg) | flagFg
+}
+
+// Background colors
+//
+// BgBlack background color (40)
+func (c Color) BgBlack() Color {
+	return (c &^ maskBg) | BlackBg
+}
+
+// BgRed background color (41)
+func (c Color) BgRed() Color {
+	return (c &^ maskBg) | RedBg
+}
+
+// BgGreen background color (42)
+func (c Color) BgGreen() Color {
+	return (c &^ maskBg) | GreenBg
+}
+
+// BgYellow background color (43)
+func (c Color) BgYellow() Color {
+	return (c &^ maskBg) | YellowBg
+}
+
+// BgBlue background color (44)
+func (c Color) BgBlue() Color {
+	return (c &^ maskBg) | BlueBg
+}
+
+// BgMagenta background color (45)
+func (c Color) BgMagenta() Color {
+	return (c &^ maskBg) | MagentaBg
+}
+
+// BgCyan background color (46)
+func (c Color) BgCyan() Color {
+	return (c &^ maskBg) | CyanBg
+}
+
+// BgWhite background color (47)
+func (c Color) BgWhite() Color {
+	return (c &^ maskBg) | WhiteBg
+}
+
+// Bright background colors
+//
+// BgBrightBlack background color (100)
+func (c Color) BgBrightBlack() Color {
+	return (c &^ maskBg) | BrightBg | BlackBg
+}
+
+// BgBrightRed background color (101)
+func (c Color) BgBrightRed() Color {
+	return (c &^ maskBg) | BrightBg | RedBg
+}
+
+// BgBrightGreen background color (102)
+func (c Color) BgBrightGreen() Color {
+	return (c &^ maskBg) | BrightBg | GreenBg
+}
+
+// BgBrightYellow background color (103)
+func (c Color) BgBrightYellow() Color {
+	return (c &^ maskBg) | BrightBg | YellowBg
+}
+
+// BgBrightBlue background color (104)
+func (c Color) BgBrightBlue() Color {
+	return (c &^ maskBg) | BrightBg | BlueBg
+}
+
+// BgBrightMagenta background color (105)
+func (c Color) BgBrightMagenta() Color {
+	return (c &^ maskBg) | BrightBg | MagentaBg
+}
+
+// BgBrightCyan background color (106)
+func (c Color) BgBrightCyan() Color {
+	return (c &^ maskBg) | BrightBg | CyanBg
+}
+
+// BgBrightWhite background color (107)
+func (c Color) BgBrightWhite() Color {
+	return (c &^ maskBg) | BrightBg | WhiteBg
+}
+
+// Other
+//
+// BgIndex of 8-bit pre-defined background color
+// from 0 to 255 (48;5;n).
+//
+//	  0-  7:  standard colors (as in ESC [ 40–47 m)
+//	  8- 15:  high intensity colors (as in ESC [100–107 m)
+//	 16-231:  6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
+//	232-255:  grayscale from black to white in 24 steps
+func (c Color) BgIndex(n ColorIndex) Color {
+	return (c &^ maskBg) | (Color(n) << shiftBg) | flagBg
+}
+
+// BgGray from 0 to 23.
+func (c Color) BgGray(n GrayIndex) Color {
+	if n > 23 {
+		n = 23
+	}
+	return (c &^ maskBg) | (Color(232+n) << shiftBg) | flagBg
+}
diff --git a/color_test.go b/color_test.go
index d25ab7a..523ee96 100644
--- a/color_test.go
+++ b/color_test.go
@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2016-2020 The Aurora Authors. All rights reserved.
+// Copyright (c) 2016-2022 The Aurora Authors. All rights reserved.
 // This program is free software. It comes without any warranty,
 // to the extent permitted by applicable law. You can redistribute
 // it and/or modify it under the terms of the Unlicense. See LICENSE
@@ -36,8 +36,11 @@
 package aurora
 
 import (
+	"math"
 	"strconv"
 	"testing"
+
+	"github.com/stretchr/testify/assert"
 )
 
 func TestColor_Nos(t *testing.T) {
@@ -142,24 +145,337 @@ func TestColor_Nos(t *testing.T) {
 					want = "0"
 				}
 			}
-			if nos != want {
-				t.Errorf("%t %d: wrong nos string %q, want %q",
-					zero, i, nos, want)
-			}
+			assert.Equalf(t, want, nos, "%t %d: wrong nos string %q, want %q",
+				zero, i, nos, want)
 		}
 	}
 }
 
-func TestColor_IsValid(t *testing.T) {
-	if Color(0).IsValid() == false {
-		t.Error("invalid")
+func Test_itoa(t *testing.T) {
+	for i := 0; i < 256; i++ {
+		var a = itoa(byte(i))
+		assert.Equalf(t, a, strconv.Itoa(i), "wrong %q, want %d", a, i)
 	}
 }
 
-func Test_itoa(t *testing.T) {
+func TestColor_Reset(t *testing.T) {
+	var c Color = math.MaxUint // all bits set to 1
+	assert.Zero(t, c.Reset())
+}
+
+func TestColor_Bold(t *testing.T) {
+	assert.True(t, Color(0).Bold()&BoldFm != 0, "not a bold")
+	assert.True(t, Color(FaintFm).Bold()&FaintFm == 0, "contains faint")
+}
+
+func TestColor_Faint(t *testing.T) {
+	assert.True(t, Color(0).Faint()&FaintFm != 0, "not a faint")
+	assert.True(t, Color(BoldFm).Faint()&BoldFm == 0, "contains bold")
+}
+
+func TestColor_DoublyUnderline(t *testing.T) {
+	assert.True(t, Color(0).DoublyUnderline()&DoublyUnderlineFm != 0,
+		"not a doubly-underlined")
+	assert.True(t, Color(UnderlineFm).DoublyUnderline()&UnderlineFm == 0,
+		"contains underline")
+}
+
+func TestColor_Fraktur(t *testing.T) {
+	assert.True(t, Color(0).Fraktur()&FrakturFm != 0, "not a fraktur")
+}
+
+func TestColor_Italic(t *testing.T) {
+	assert.True(t, Color(0).Italic()&ItalicFm != 0, "not a italic")
+}
+
+func TestColor_Underline(t *testing.T) {
+	assert.True(t, Color(0).Underline()&UnderlineFm != 0, "not a underlined")
+}
+
+func TestColor_SlowBlink(t *testing.T) {
+	assert.True(t, Color(0).SlowBlink()&SlowBlinkFm != 0, "not a slow blinking")
+}
+
+func TestColor_RapidBlink(t *testing.T) {
+	assert.True(t, Color(0).RapidBlink()&RapidBlinkFm != 0,
+		"not a rapid blinking")
+}
+
+func TestColor_Blink(t *testing.T) {
+	assert.True(t, Color(0).Blink()&BlinkFm != 0, "not a blinking")
+}
+
+func TestColor_Reverse(t *testing.T) {
+	assert.True(t, Color(0).Reverse()&ReverseFm != 0, "not a reversed")
+}
+
+func TestColor_Inverse(t *testing.T) {
+	assert.True(t, Color(0).Inverse()&InverseFm != 0, "not a inversed")
+}
+
+func TestColor_Conceal(t *testing.T) {
+	assert.True(t, Color(0).Conceal()&ConcealFm != 0, "not a concealed")
+}
+
+func TestColor_Hidden(t *testing.T) {
+	assert.True(t, Color(0).Hidden()&HiddenFm != 0, "not a hidden")
+}
+
+func TestColor_CrossedOut(t *testing.T) {
+	assert.True(t, Color(0).CrossedOut()&CrossedOutFm != 0, "not a crossed out")
+}
+
+func TestColor_StrikeThrough(t *testing.T) {
+	assert.True(t, Color(0).StrikeThrough()&StrikeThroughFm != 0,
+		"not a striked through")
+}
+
+func TestColor_Framed(t *testing.T) {
+	assert.True(t, Color(0).Framed()&FramedFm != 0, "not a framed")
+}
+
+func TestColor_Encircled(t *testing.T) {
+	assert.True(t, Color(0).Encircled()&EncircledFm != 0, "not a encircled")
+}
+
+func TestColor_Overlined(t *testing.T) {
+	assert.True(t, Color(0).Overlined()&OverlinedFm != 0, "not a overlined")
+}
+
+func TestColor_Black(t *testing.T) {
+	assert.True(t, Color(0).Black()&BlackFg != 0, "not a black")
+	assert.True(t, Color(RedFg).Black()&RedFg == flagFg, "contains red")
+}
+
+func TestColor_Red(t *testing.T) {
+	assert.True(t, Color(0).Red()&RedFg != 0, "not a red")
+	assert.True(t, Color(BlackFg).Red()&BlackFg == flagFg, "contains black")
+}
+
+func TestColor_Green(t *testing.T) {
+	assert.True(t, Color(0).Green()&GreenFg != 0, "not a green")
+	assert.True(t, Color(BlackFg).Green()&BlackFg == flagFg, "contains black")
+}
+
+func TestColor_Yellow(t *testing.T) {
+	assert.True(t, Color(0).Yellow()&YellowFg != 0, "not a yellow")
+	assert.True(t, Color(BlackFg).Yellow()&BlackFg == flagFg, "contains black")
+}
+
+func TestColor_Blue(t *testing.T) {
+	assert.True(t, Color(0).Blue()&BlueFg != 0, "not a blue")
+	assert.True(t, Color(BlackFg).Blue()&BlackFg == flagFg, "contains black")
+}
+
+func TestColor_Magenta(t *testing.T) {
+	assert.True(t, Color(0).Magenta()&MagentaFg != 0, "not a magenta")
+	assert.True(t, Color(BlackFg).Magenta()&BlackFg == flagFg, "contains black")
+}
+
+func TestColor_Cyan(t *testing.T) {
+	assert.True(t, Color(0).Cyan()&CyanFg != 0, "not a cyan")
+	assert.True(t, Color(BlackFg).Cyan()&BlackFg == flagFg, "contains black")
+}
+
+func TestColor_White(t *testing.T) {
+	assert.True(t, Color(0).White()&WhiteFg != 0, "not a white")
+	assert.True(t, Color(BlackFg).White()&BlackFg == flagFg, "contains black")
+}
+
+func TestColor_BrightBlack(t *testing.T) {
+	assert.True(t, Color(0).BrightBlack()&(BrightFg|BlackFg) != 0,
+		"not a bright black")
+	assert.True(t, Color(RedFg).BrightBlack()&RedFg == flagFg, "contains red")
+}
+
+func TestColor_BrightRed(t *testing.T) {
+	assert.True(t, Color(0).BrightRed()&(BrightFg|RedFg) != 0,
+		"not a bright red")
+	assert.True(t, Color(BlackFg).BrightRed()&BlackFg == flagFg,
+		"contains black")
+}
+
+func TestColor_BrightGreen(t *testing.T) {
+	assert.True(t, Color(0).BrightGreen()&(BrightFg|GreenFg) != 0,
+		"not a bright green")
+	assert.True(t, Color(BlackFg).BrightGreen()&BlackFg == flagFg,
+		"contains black")
+}
+
+func TestColor_BrightYellow(t *testing.T) {
+	assert.True(t, Color(0).BrightYellow()&(BrightFg|YellowFg) != 0,
+		"not a bright yellow")
+	assert.True(t, Color(BlackFg).BrightYellow()&BlackFg == flagFg,
+		"contains black")
+}
+
+func TestColor_BrightBlue(t *testing.T) {
+	assert.True(t, Color(0).BrightBlue()&(BrightFg|BlueFg) != 0,
+		"not a bright blue")
+	assert.True(t, Color(BlackFg).BrightBlue()&BlackFg == flagFg,
+		"contains black")
+}
+
+func TestColor_BrightMagenta(t *testing.T) {
+	assert.True(t, Color(0).BrightMagenta()&(BrightFg|MagentaFg) != 0,
+		"not a bright blue")
+	assert.True(t, Color(BlackFg).BrightMagenta()&BlackFg == flagFg,
+		"contains black")
+}
+
+func TestColor_BrightCyan(t *testing.T) {
+	assert.True(t, Color(0).BrightCyan()&(BrightFg|CyanFg) != 0,
+		"not a bright cyan")
+	assert.True(t, Color(BlackFg).BrightCyan()&BlackFg == flagFg,
+		"contains black")
+}
+
+func TestColor_BrightWhite(t *testing.T) {
+	assert.True(t, Color(0).BrightWhite()&(BrightFg|WhiteFg) != 0,
+		"not a bright white")
+	assert.True(t, Color(BlackFg).BrightWhite()&BlackFg == flagFg,
+		"contains black")
+}
+
+func TestColor_Index(t *testing.T) {
 	for i := 0; i < 256; i++ {
-		if a := itoa(byte(i)); a != strconv.Itoa(i) {
-			t.Errorf("wrong %q, want %d", a, i)
-		}
+		var ci = ColorIndex(i)
+		assert.Truef(t, Color(0).Index(ci)&flagFg != 0,
+			"missing indexed color, color index %d", i)
+		assert.Truef(t, Color(BlackFg).Index(ci)&BlackFg == flagFg,
+			"contains black, color index %d", i)
+	}
+}
+
+func TestColor_Gray(t *testing.T) {
+	for i := GrayIndex(0); i < 25; i++ {
+		assert.Truef(t, Color(0).Gray(i)&flagFg != 0,
+			"missing indexed gray color, gray index %d", i)
+		assert.Truef(t, Color(BlackFg).Gray(i)&BlackFg == flagFg,
+			"contains black, gray index %d", i)
+	}
+}
+
+func TestColor_BgBlack(t *testing.T) {
+	assert.True(t, Color(0).BgBlack()&BlackBg != 0, "not a black background")
+	assert.True(t, Color(RedBg).BgBlack()&RedBg == flagBg,
+		"contains red background")
+}
+
+func TestColor_BgRed(t *testing.T) {
+	assert.True(t, Color(0).BgRed()&RedBg != 0, "not a red background")
+	assert.True(t, Color(BlackBg).BgRed()&BlackBg == flagBg,
+		"contains black background")
+}
+
+func TestColor_BgGreen(t *testing.T) {
+	assert.True(t, Color(0).BgGreen()&GreenBg != 0, "not a green background")
+	assert.True(t, Color(BlackBg).BgGreen()&BlackBg == flagBg,
+		"contains black background")
+}
+
+func TestColor_BgYellow(t *testing.T) {
+	assert.True(t, Color(0).BgYellow()&YellowBg != 0, "not a yellow background")
+	assert.True(t, Color(BlackBg).BgYellow()&BlackBg == flagBg,
+		"contains black background")
+}
+
+func TestColor_BgBlue(t *testing.T) {
+	assert.True(t, Color(0).BgBlue()&BlueBg != 0, "not a blue background")
+	assert.True(t, Color(BlackBg).BgBlue()&BlackBg == flagBg,
+		"contains black background")
+}
+
+func TestColor_BgMagenta(t *testing.T) {
+	assert.True(t, Color(0).BgMagenta()&MagentaBg != 0,
+		"not a magenta background")
+	assert.True(t, Color(BlackBg).BgMagenta()&BlackBg == flagBg,
+		"contains black background")
+}
+
+func TestColor_BgCyan(t *testing.T) {
+	assert.True(t, Color(0).BgCyan()&CyanBg != 0, "not a cyan background")
+	assert.True(t, Color(BlackBg).BgCyan()&BlackBg == flagBg,
+		"contains black background")
+}
+
+func TestColor_BgWhite(t *testing.T) {
+	assert.True(t, Color(0).BgWhite()&WhiteBg != 0, "not a white background")
+	assert.True(t, Color(BlackBg).BgWhite()&BlackBg == flagBg,
+		"contains black background")
+}
+
+func TestColor_BgBrightBlack(t *testing.T) {
+	assert.True(t, Color(0).BgBrightBlack()&(BrightBg|BlackBg) != 0,
+		"not a bright black background")
+	assert.True(t, Color(RedBg).BgBrightBlack()&RedBg == flagBg,
+		"contains red background")
+}
+
+func TestColor_BgBrightRed(t *testing.T) {
+	assert.True(t, Color(0).BgBrightRed()&(BrightBg|RedBg) != 0,
+		"not a bright red background")
+	assert.True(t, Color(BlackBg).BgBrightRed()&BlackBg == flagBg,
+		"contains black background")
+}
+
+func TestColor_BgBrightGreen(t *testing.T) {
+	assert.True(t, Color(0).BgBrightGreen()&(BrightBg|GreenBg) != 0,
+		"not a bright green background")
+	assert.True(t, Color(BlackBg).BgBrightGreen()&BlackBg == flagBg,
+		"contains black background")
+}
+
+func TestColor_BgBrightYellow(t *testing.T) {
+	assert.True(t, Color(0).BgBrightYellow()&(BrightBg|YellowBg) != 0,
+		"not a bright yellow background")
+	assert.True(t, Color(BlackBg).BgBrightYellow()&BlackBg == flagBg,
+		"contains black background")
+}
+
+func TestColor_BgBrightBlue(t *testing.T) {
+	assert.True(t, Color(0).BgBrightBlue()&(BrightBg|BlueBg) != 0,
+		"not a bright blue background")
+	assert.True(t, Color(BlackBg).BgBrightBlue()&BlackBg == flagBg,
+		"contains black background")
+}
+
+func TestColor_BgBrightMagenta(t *testing.T) {
+	assert.True(t, Color(0).BgBrightMagenta()&(BrightBg|MagentaBg) != 0,
+		"not a bright magenta background")
+	assert.True(t, Color(BlackBg).BgBrightMagenta()&BlackBg == flagBg,
+		"contains black background")
+}
+
+func TestColor_BgBrightCyan(t *testing.T) {
+	assert.True(t, Color(0).BgBrightCyan()&(BrightBg|CyanBg) != 0,
+		"not a bright cyan background")
+	assert.True(t, Color(BlackBg).BgBrightCyan()&BlackBg == flagBg,
+		"contains black background")
+}
+
+func TestColor_BgBrightWhite(t *testing.T) {
+	assert.True(t, Color(0).BgBrightWhite()&(BrightBg|WhiteBg) != 0,
+		"not a bright white background")
+	assert.True(t, Color(BlackBg).BgBrightWhite()&BlackBg == flagBg,
+		"contains black background")
+}
+
+func TestColor_BgIndex(t *testing.T) {
+	for i := 0; i < 256; i++ {
+		var ci = ColorIndex(i)
+		assert.True(t, Color(0).BgIndex(ci)&(flagBg) != 0,
+			"missing indexed background color")
+		assert.True(t, Color(BlackBg).BgIndex(ci)&BlackBg == flagBg,
+			"contains black background")
+	}
+}
+
+func TestColor_BgGray(t *testing.T) {
+	for i := GrayIndex(0); i <= 25; i++ {
+		assert.Truef(t, Color(0).BgGray(i)&(flagBg) != 0,
+			"missing indexed gray background color, gray index %d", i)
+		assert.Truef(t, Color(BlackBg).BgGray(i)&BlackBg == flagBg,
+			"contains black background, gray index %d", i)
 	}
 }
diff --git a/config.go b/config.go
new file mode 100644
index 0000000..f659de5
--- /dev/null
+++ b/config.go
@@ -0,0 +1,120 @@
+//
+// Copyright (c) 2016-2022 The Aurora Authors. All rights reserved.
+// This program is free software. It comes without any warranty,
+// to the extent permitted by applicable law. You can redistribute
+// it and/or modify it under the terms of the Unlicense. See LICENSE
+// file for more details or see below.
+//
+
+//
+// This is free and unencumbered software released into the public domain.
+//
+// Anyone is free to copy, modify, publish, use, compile, sell, or
+// distribute this software, either in source code form or as a compiled
+// binary, for any purpose, commercial or non-commercial, and by any
+// means.
+//
+// In jurisdictions that recognize copyright laws, the author or authors
+// of this software dedicate any and all copyright interest in the
+// software to the public domain. We make this dedication for the benefit
+// of the public at large and to the detriment of our heirs and
+// successors. We intend this dedication to be an overt act of
+// relinquishment in perpetuity of all present and future rights to this
+// software under copyright law.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+// For more information, please refer to <http://unlicense.org/>
+//
+
+package aurora
+
+import "flag"
+
+// Config represents configurations of a colorizer.
+type Config struct {
+	// Colors feature. Enable colors if true.
+	Colors bool `json:"colors" yaml:"colors" toml:"colors" mapstructure:"colors"`
+	// Hyperlinks feature. Enable hyperlinks if true.
+	Hyperlinks bool `json:"hyperlinks" yaml:"hyperlinks" toml:"hyperlinks" mapstructure:"hyperlinks"`
+}
+
+// NewConfig returns new default Config.
+func NewConfig() (conf Config) {
+	conf.Colors = true
+	conf.Hyperlinks = true
+	return
+}
+
+// AddFlags to given *flag.FlagSet. The prefix used as prefix for flags.
+// It may be used to parse commandline flags. For example
+//
+//	var conf Config
+//	conf.AddFlags(flag.CommandLine, "colors.")
+//	flag.Parse()
+//
+// for a main package, and use with flags commandline flags,
+//
+//	go run main.go -colors.colors -colors.hyperlinks
+//
+// to enable or disable features. A colorizer can be created, for example,
+//
+//	var colorizer = New(conf.Options()...)
+func (c *Config) AddFlags(fset *flag.FlagSet, prefix string) {
+	fset.BoolVar(&c.Colors,
+		prefix+"colors",
+		c.Colors,
+		"enable colors")
+	fset.BoolVar(&c.Hyperlinks,
+		prefix+"hyperlinks",
+		c.Hyperlinks,
+		"enable hyperlinks")
+}
+
+// Apply given options for the Config.
+func (c *Config) Apply(opts ...Option) {
+	for _, opt := range opts {
+		opt(c)
+	}
+}
+
+// Options by the Config.
+func (c *Config) Options() (opts []Option) {
+	return []Option{
+		WithColors(c.Colors),
+		WithHyperlinks(c.Hyperlinks),
+	}
+}
+
+func (c *Config) colorConfig() (cc colorConfig) {
+	if c.Colors {
+		cc |= colorPin
+	}
+	if c.Hyperlinks {
+		cc |= hyperlinksPin
+	}
+	return
+}
+
+// An Option function.
+type Option func(*Config)
+
+// WithColors is an Option that used to enable or disable colors.
+func WithColors(t bool) Option {
+	return func(c *Config) {
+		c.Colors = t
+	}
+}
+
+// WithHyperlinks is an Option that used to enable or disable links.
+func WithHyperlinks(t bool) Option {
+	return func(c *Config) {
+		c.Hyperlinks = t
+	}
+}
diff --git a/config_test.go b/config_test.go
new file mode 100644
index 0000000..5eae665
--- /dev/null
+++ b/config_test.go
@@ -0,0 +1,157 @@
+//
+// Copyright (c) 2016-2022 The Aurora Authors. All rights reserved.
+// This program is free software. It comes without any warranty,
+// to the extent permitted by applicable law. You can redistribute
+// it and/or modify it under the terms of the Unlicense. See LICENSE
+// file for more details or see below.
+//
+
+//
+// This is free and unencumbered software released into the public domain.
+//
+// Anyone is free to copy, modify, publish, use, compile, sell, or
+// distribute this software, either in source code form or as a compiled
+// binary, for any purpose, commercial or non-commercial, and by any
+// means.
+//
+// In jurisdictions that recognize copyright laws, the author or authors
+// of this software dedicate any and all copyright interest in the
+// software to the public domain. We make this dedication for the benefit
+// of the public at large and to the detriment of our heirs and
+// successors. We intend this dedication to be an overt act of
+// relinquishment in perpetuity of all present and future rights to this
+// software under copyright law.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+// For more information, please refer to <http://unlicense.org/>
+//
+
+package aurora
+
+import (
+	"flag"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestNewConfig(t *testing.T) {
+	assert.Equal(t, Config{
+		Colors:     true,
+		Hyperlinks: true,
+	}, NewConfig())
+}
+
+func TestConfig_AddFlags(t *testing.T) {
+	// set to true
+	var fset = flag.NewFlagSet("x", flag.ContinueOnError)
+	var conf = NewConfig()
+	conf.AddFlags(fset, "testing.")
+	var err = fset.Parse([]string{
+		"-testing.colors",
+		"-testing.hyperlinks",
+	})
+	require.NoError(t, err)
+	assert.True(t, conf.Colors)
+	assert.True(t, conf.Hyperlinks)
+	// set to true & false
+	fset = flag.NewFlagSet("x", flag.ContinueOnError)
+	conf.AddFlags(fset, "testing.")
+	err = fset.Parse([]string{
+		"-testing.colors=t",
+		"-testing.hyperlinks=f",
+	})
+	require.NoError(t, err)
+	assert.True(t, conf.Colors)
+	assert.False(t, conf.Hyperlinks)
+	// set to false & true
+	fset = flag.NewFlagSet("x", flag.ContinueOnError)
+	conf.AddFlags(fset, "testing.")
+	err = fset.Parse([]string{
+		"-testing.colors=f",
+		"-testing.hyperlinks=t",
+	})
+	require.NoError(t, err)
+	assert.False(t, conf.Colors)
+	assert.True(t, conf.Hyperlinks)
+	// set to false
+	fset = flag.NewFlagSet("x", flag.ContinueOnError)
+	conf.AddFlags(fset, "testing.")
+	err = fset.Parse([]string{
+		"-testing.colors=f",
+		"-testing.hyperlinks=f",
+	})
+	require.NoError(t, err)
+	assert.False(t, conf.Colors)
+	assert.False(t, conf.Hyperlinks)
+}
+
+func TestConfig_Apply(t *testing.T) {
+	var conf = NewConfig()
+	conf.Apply(WithColors(false), WithHyperlinks(false))
+	assert.Equal(t, Config{
+		Colors:     false,
+		Hyperlinks: false,
+	}, conf)
+}
+
+func TestConfig_Options(t *testing.T) {
+	var (
+		c1 = NewConfig()
+		c2 Config
+	)
+	c2.Apply(c1.Options()...)
+	assert.Equal(t, Config{
+		Colors:     true,
+		Hyperlinks: true,
+	}, c2)
+}
+
+func TestConfig_colorConfig(t *testing.T) {
+	var conf = NewConfig()
+	assert.Equal(t, colorPin|hyperlinksPin, conf.colorConfig())
+	conf.Colors = false
+	assert.Equal(t, hyperlinksPin, conf.colorConfig())
+	conf.Hyperlinks = false
+	assert.Equal(t, colorConfig(0), conf.colorConfig())
+}
+
+func TestWithColors(t *testing.T) {
+	var conf Config
+	// turn to true
+	conf.Apply(WithColors(true))
+	assert.Equal(t, Config{
+		Colors:     true,
+		Hyperlinks: false,
+	}, conf)
+	// turn to false
+	conf.Apply(WithColors(false))
+	assert.Equal(t, Config{
+		Colors:     false,
+		Hyperlinks: false,
+	}, conf)
+}
+
+func TestWithHyperlinks(t *testing.T) {
+	var conf Config
+	// turn to true
+	conf.Apply(WithHyperlinks(true))
+	assert.Equal(t, Config{
+		Colors:     false,
+		Hyperlinks: true,
+	}, conf)
+	// turn to false
+	conf.Apply(WithHyperlinks(false))
+	assert.Equal(t, Config{
+		Colors:     false,
+		Hyperlinks: false,
+	}, conf)
+}
diff --git a/debian/changelog b/debian/changelog
index 69218ad..8ef2c3a 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+golang-github-logrusorgru-aurora (4.0.0-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Wed, 14 Dec 2022 18:15:44 -0000
+
 golang-github-logrusorgru-aurora (3.0.0-2) unstable; urgency=medium
 
   [ Nilesh Patra ]
diff --git a/example_test.go b/example_test.go
index 9ad16f2..317f47d 100644
--- a/example_test.go
+++ b/example_test.go
@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2016-2019 The Aurora Authors. All rights reserved.
+// Copyright (c) 2016-2022 The Aurora Authors. All rights reserved.
 // This program is free software. It comes without any warranty,
 // to the extent permitted by applicable law. You can redistribute
 // it and/or modify it under the terms of the Unlicense. See LICENSE
@@ -7,19 +7,30 @@
 //
 
 //
-//        DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
-//                    Version 2, December 2004
+// This is free and unencumbered software released into the public domain.
 //
-// Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
+// Anyone is free to copy, modify, publish, use, compile, sell, or
+// distribute this software, either in source code form or as a compiled
+// binary, for any purpose, commercial or non-commercial, and by any
+// means.
 //
-// Everyone is permitted to copy and distribute verbatim or modified
-// copies of this license document, and changing it is allowed as long
-// as the name is changed.
+// In jurisdictions that recognize copyright laws, the author or authors
+// of this software dedicate any and all copyright interest in the
+// software to the public domain. We make this dedication for the benefit
+// of the public at large and to the detriment of our heirs and
+// successors. We intend this dedication to be an overt act of
+// relinquishment in perpetuity of all present and future rights to this
+// software under copyright law.
 //
-//            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
-//   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
 //
-//  0. You just DO WHAT THE FUCK YOU WANT TO.
+// For more information, please refer to <http://unlicense.org/>
 //
 
 package aurora
@@ -40,15 +51,15 @@ func ExampleBold() {
 	// Output: value: 99
 }
 
-func ExampleNewAurora_no_colors() {
-	a := NewAurora(false)
+func ExampleNew_no_colors() {
+	var a = New(WithColors(false), WithHyperlinks(false))
 	fmt.Println(a.Red("Not red"))
 
 	// Output: Not red
 }
 
-func ExampleNewAurora_colors() {
-	a := NewAurora(true)
+func ExampleNew_colors() {
+	var a = New()
 	fmt.Println(a.Red("Red"))
 
 	// Output: Red
@@ -71,3 +82,9 @@ func ExampleSprintf() {
 
 	// Output: we've got 5 cats, but want 25
 }
+
+func ExampleHyperlink() {
+	fmt.Println(Hyperlink(Red("Example"), "http://example.com/"))
+
+	// Output: ]8;;http://example.com/\Example]8;;\
+}
diff --git a/go.mod b/go.mod
index bc595b2..c1e237c 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,11 @@
-module github.com/logrusorgru/aurora/v3
+module github.com/logrusorgru/aurora/v4
 
-go 1.14
+go 1.19
+
+require github.com/stretchr/testify v1.8.0
+
+require (
+	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/pmezard/go-difflib v1.0.0 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..5164829
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,15 @@
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/hyperlinks.go b/hyperlinks.go
new file mode 100644
index 0000000..929159c
--- /dev/null
+++ b/hyperlinks.go
@@ -0,0 +1,283 @@
+//
+// Copyright (c) 2016-2022 The Aurora Authors. All rights reserved.
+// This program is free software. It comes without any warranty,
+// to the extent permitted by applicable law. You can redistribute
+// it and/or modify it under the terms of the Unlicense. See LICENSE
+// file for more details or see below.
+//
+
+//
+// This is free and unencumbered software released into the public domain.
+//
+// Anyone is free to copy, modify, publish, use, compile, sell, or
+// distribute this software, either in source code form or as a compiled
+// binary, for any purpose, commercial or non-commercial, and by any
+// means.
+//
+// In jurisdictions that recognize copyright laws, the author or authors
+// of this software dedicate any and all copyright interest in the
+// software to the public domain. We make this dedication for the benefit
+// of the public at large and to the detriment of our heirs and
+// successors. We intend this dedication to be an overt act of
+// relinquishment in perpetuity of all present and future rights to this
+// software under copyright law.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+// For more information, please refer to <http://unlicense.org/>
+//
+
+package aurora
+
+import (
+	"fmt"
+	"io"
+)
+
+const (
+	linkStartEsc  = "\033]8;"
+	linkMiddleEsc = "\033\\"
+	linkEndEsc    = linkStartEsc + ";" + linkMiddleEsc
+)
+
+// Hyperlinks related constants
+const (
+	HyperlinkIDKey = "id" // hyperlink id parameter key
+)
+
+// The HyperlinkParam represents a hyperlink parameter.
+type HyperlinkParam struct {
+	Key   string // parameter name
+	Value string // parameter value
+}
+
+func (hp HyperlinkParam) stringLen() int {
+	return len(hp.Key) + 1 + len(hp.Value)
+}
+
+// String represents the HyperlinkParam as string, e.g. in key=value form.
+func (hp HyperlinkParam) String() string {
+	return hp.Key + "=" + hp.Value
+}
+
+// IsValidHyperlinkTarget returns true if the target contains symbols only
+// in 32-126 ASCII range. All symbols outside this range should be URL-escaped.
+func IsValidHyperlinkTarget(target string) (valid bool) {
+	// we can walk over bytes, without Unicode runes decoding
+	for i := 0; i < len(target); i++ {
+		if target[i] < 32 || 126 < target[i] {
+			return // false, should be URL-escaped
+		}
+	}
+	return true
+}
+
+// range over bytes (not Unicode runes) and check
+func containsAny(in string, any ...byte) (contains bool) {
+	for i := 0; i < len(in); i++ {
+		for _, b := range any {
+			if in[i] == b {
+				return true
+			}
+		}
+	}
+	return // false
+}
+
+// IsValidHyperlinkParam returns true for given string, if the string
+// is valid hyperlink target (see IsValidHyperlinkTarget) and doesn't
+// contains ':', ';' and '='.
+func IsValidHyperlinkParam(param string) (valid bool) {
+	return IsValidHyperlinkTarget(param) && !containsAny(param, ':', ';', '=')
+}
+
+// HyperlinkID returns list of HyperlinkParams that contains only id parameter
+// with given value of the id parameter.
+func HyperlinkID(id string) HyperlinkParam {
+	return HyperlinkParam{
+		Key:   HyperlinkIDKey,
+		Value: id,
+	}
+}
+
+type hyperlink struct {
+	target string           // hyperlink target
+	params []HyperlinkParam // hyperlink parameters
+}
+
+func (h *hyperlink) isExists() (ok bool) {
+	if h == nil {
+		return // does not exist
+	}
+	return h.target != ""
+}
+
+func (h *hyperlink) stringParamsLen() (ln int) {
+	for i, p := range h.params {
+		if i > 0 {
+			ln++ // + colon separator
+		}
+		ln += p.stringLen()
+	}
+	return
+}
+
+func (h *hyperlink) headLen() int {
+	return len(linkStartEsc) +
+		h.stringParamsLen() +
+		len(";") +
+		len(h.target) +
+		+len(linkMiddleEsc)
+}
+
+func (h *hyperlink) headBytes() (t []byte) {
+	t = make([]byte, 0, h.headLen())
+
+	t = append(t, linkStartEsc...)
+	for i, param := range h.params {
+		if i > 0 {
+			t = append(t, ':')
+		}
+		t = append(t, param.Key...)
+		t = append(t, '=')
+		t = append(t, param.Value...)
+	}
+	t = append(t, ';')
+	t = append(t, h.target...)
+	t = append(t, linkMiddleEsc...)
+	return
+}
+
+func (h *hyperlink) tailLen() int {
+	return len(linkEndEsc)
+}
+
+func (h *hyperlink) tailBytes() []byte {
+	return []byte(linkEndEsc)
+}
+
+func (h *hyperlink) writeHead(w io.Writer) {
+	if h == nil || h.target == "" {
+		return
+	}
+	w.Write(h.headBytes()) //nolint
+}
+
+func (h *hyperlink) writeTail(w io.Writer) {
+	if h == nil || h.target == "" {
+		return
+	}
+	w.Write(h.tailBytes()) //nolint
+}
+
+func shouldEscape(c byte) bool {
+	return c < 32 || 126 < c
+}
+
+func isHex(c byte) bool {
+	switch {
+	case '0' <= c && c <= '9':
+		return true
+	case 'a' <= c && c <= 'f':
+		return true
+	case 'A' <= c && c <= 'F':
+		return true
+	}
+	return false
+}
+
+func unhex(c byte) byte {
+	switch {
+	case '0' <= c && c <= '9':
+		return c - '0'
+	case 'a' <= c && c <= 'f':
+		return c - 'a' + 10
+	case 'A' <= c && c <= 'F':
+		return c - 'A' + 10
+	}
+	return 0
+}
+
+// HyperlinkEscape escapes all symbols of given string out of [32; 126] range
+// using URL-encoding. Used to escape a hyperlink target.
+func HyperlinkEscape(s string) string {
+
+	var hexCount int
+	for i := 0; i < len(s); i++ {
+		if shouldEscape(s[i]) {
+			hexCount++
+		}
+	}
+
+	if hexCount == 0 {
+		return s
+	}
+
+	const upperhex = "0123456789ABCDEF"
+
+	var (
+		t = make([]byte, len(s)+2*hexCount)
+		j int
+	)
+
+	for i := 0; i < len(s); i++ {
+		switch c := s[i]; {
+		case shouldEscape(c):
+			t[j] = '%'
+			t[j+1] = upperhex[c>>4]
+			t[j+2] = upperhex[c&15]
+			j += 3
+		default:
+			t[j] = s[i]
+			j++
+		}
+	}
+
+	return string(t)
+}
+
+// HyperlinkUnescape reverts a string escaped by the HyperlinkEscape.
+func HyperlinkUnescape(s string) (raw string, err error) {
+
+	var n int
+	for i := 0; i < len(s); {
+		switch s[i] {
+		case '%':
+			n++
+			if i+2 >= len(s) || !isHex(s[i+1]) || !isHex(s[i+2]) {
+				s = s[i:]
+				if len(s) > 3 {
+					s = s[:3]
+				}
+				return "", fmt.Errorf("invalid URL-escape sequence: %q", s)
+			}
+			i += 3
+		default:
+			i++
+		}
+	}
+
+	if n == 0 {
+		return s, nil
+	}
+
+	var t = make([]byte, 0, len(s)-2*n)
+
+	for i := 0; i < len(s); i++ {
+		switch s[i] {
+		case '%':
+			t = append(t, unhex(s[i+1])<<4|unhex(s[i+2]))
+			i += 2
+		default:
+			t = append(t, s[i])
+		}
+	}
+
+	return string(t), nil
+}
diff --git a/hyperlinks_test.go b/hyperlinks_test.go
new file mode 100644
index 0000000..74f2225
--- /dev/null
+++ b/hyperlinks_test.go
@@ -0,0 +1,119 @@
+//
+// Copyright (c) 2016-2022 The Aurora Authors. All rights reserved.
+// This program is free software. It comes without any warranty,
+// to the extent permitted by applicable law. You can redistribute
+// it and/or modify it under the terms of the Unlicense. See LICENSE
+// file for more details or see below.
+//
+
+//
+// This is free and unencumbered software released into the public domain.
+//
+// Anyone is free to copy, modify, publish, use, compile, sell, or
+// distribute this software, either in source code form or as a compiled
+// binary, for any purpose, commercial or non-commercial, and by any
+// means.
+//
+// In jurisdictions that recognize copyright laws, the author or authors
+// of this software dedicate any and all copyright interest in the
+// software to the public domain. We make this dedication for the benefit
+// of the public at large and to the detriment of our heirs and
+// successors. We intend this dedication to be an overt act of
+// relinquishment in perpetuity of all present and future rights to this
+// software under copyright law.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+// For more information, please refer to <http://unlicense.org/>
+//
+
+package aurora
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestHyperlinkParam_stringLen(t *testing.T) {
+	var param HyperlinkParam
+	assert.Equal(t, 1, param.stringLen())
+	param.Key, param.Value = "some", "thing"
+	assert.Equal(t, 1+len("some")+len("thing"), param.stringLen())
+}
+
+func TestHyperlinkParam_String(t *testing.T) {
+	var param HyperlinkParam
+	assert.Equal(t, "=", param.String())
+	param.Key, param.Value = "some", "thing"
+	assert.Equal(t, "some=thing", param.String())
+}
+
+func TestIsValidHyperlinkTarget(t *testing.T) {
+	assert.True(t, IsValidHyperlinkTarget("http://example.com/path?query=true"))
+	assert.True(t, IsValidHyperlinkTarget("mailto:user@example.com"))
+	assert.False(t, IsValidHyperlinkTarget("http://пример.тест/путь?запрос=да"))
+	assert.False(t, IsValidHyperlinkTarget("mailto:пользователь@пример.тест"))
+}
+
+func TestIsValidHyperlinkParam(t *testing.T) {
+	assert.True(t, IsValidHyperlinkParam("id"))
+	assert.True(t, IsValidHyperlinkParam("a_param-key"))
+	assert.False(t, IsValidHyperlinkParam("value:true"))
+	assert.False(t, IsValidHyperlinkParam("value=true"))
+	assert.False(t, IsValidHyperlinkParam("v1;v2"))
+}
+
+func TestHyperlinkID(t *testing.T) {
+	assert.Equal(t, HyperlinkParam{
+		Key:   HyperlinkIDKey,
+		Value: "value",
+	}, HyperlinkID("value"))
+}
+
+func Test_unhex(t *testing.T) {
+	assert.Zero(t, unhex(0))
+}
+
+func TestHyperlinkEscape(t *testing.T) {
+	var val = "http://example.com/path?query=true"
+	assert.Equal(t, val, HyperlinkEscape(val))
+	val = "mailto:user@example.com"
+	assert.Equal(t, val, HyperlinkEscape(val))
+	val = "http://пример.тест/путь?запрос=да"
+	assert.True(t, IsValidHyperlinkTarget(HyperlinkEscape(val)))
+	val = "mailto:пользователь@пример.тест"
+	assert.True(t, IsValidHyperlinkTarget(HyperlinkEscape(val)))
+}
+
+func TestHyperlinkUnescape(t *testing.T) {
+	for _, val := range []string{
+		"http://example.com/path?query=true",
+		"mailto:user@example.com",
+		"http://пример.тест/путь?запрос=да",
+		"mailto:пользователь@пример.тест",
+	} {
+		var got, err = HyperlinkUnescape(HyperlinkEscape(val))
+		require.NoError(t, err)
+		assert.Equal(t, val, got)
+	}
+	var _, err = HyperlinkUnescape("%%%%")
+	assert.Error(t, err)
+	var (
+		val     = "значение"
+		escaped = HyperlinkEscape(val)
+		back    string
+	)
+	escaped = strings.ToLower(escaped)
+	back, err = HyperlinkUnescape(escaped)
+	assert.NoError(t, err)
+	assert.Equal(t, val, back)
+}
diff --git a/sprintf.go b/sprintf.go
index b92d593..b1389a6 100644
--- a/sprintf.go
+++ b/sprintf.go
@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2016-2020 The Aurora Authors. All rights reserved.
+// Copyright (c) 2016-2022 The Aurora Authors. All rights reserved.
 // This program is free software. It comes without any warranty,
 // to the extent permitted by applicable law. You can redistribute
 // it and/or modify it under the terms of the Unlicense. See LICENSE
@@ -37,27 +37,89 @@ package aurora
 
 import (
 	"fmt"
+	"strconv"
+	"unicode/utf8"
 )
 
-// Sprintf allows to use Value as format. For example
-//
-//    v := Sprintf(Red("total: +3.5f points"), Blue(3.14))
-//
-// In this case "total:" and "points" will be red, but
-// 3.14 will be blue. But, in another example
-//
-//    v := Sprintf(Red("total: +3.5f points"), 3.14)
-//
-// full string will be red. And no way to clear 3.14 to
-// default format and color
-func Sprintf(format interface{}, args ...interface{}) string {
+type tailedValue struct {
+	Value
+	tail Color
+}
+
+func (v *tailedValue) Format(s fmt.State, verb rune) {
+
+	// it's enough for many cases (%-+020.10f)
+	// %          - 1
+	// availFlags - 3 (5)
+	// width      - 2
+	// prec       - 3 (.23)
+	// verb       - 1
+	// --------------
+	//             10
+	// +
+	// \033[                            5
+	// 0;1;3;4;5;7;8;9;20;21;51;52;53  30
+	// 38;5;216                         8
+	// 48;5;216                         8
+	// m                                1
+	// +
+	// \033[0m                          7
+	//
+	// x2 (possible tail color)
+	//
+	// 10 + 59 * 2 = 128
+
+	var (
+		format = make([]byte, 0, 128)
+		color  = v.Color()
+	)
+	if color != 0 {
+		format = append(format, esc...)
+		format = color.appendNos(format, v.tail != 0)
+		format = append(format, 'm')
+	}
+	format = append(format, '%')
+	var f byte
+	for i := 0; i < len(availFlags); i++ {
+		if f = availFlags[i]; s.Flag(int(f)) {
+			format = append(format, f)
+		}
+	}
+	var width, prec int
+	var ok bool
+	if width, ok = s.Width(); ok {
+		format = strconv.AppendInt(format, int64(width), 10)
+	}
+	if prec, ok = s.Precision(); ok {
+		format = append(format, '.')
+		format = strconv.AppendInt(format, int64(prec), 10)
+	}
+	if verb > utf8.RuneSelf {
+		format = append(format, string(verb)...)
+	} else {
+		format = append(format, byte(verb))
+	}
+	if color != 0 {
+		if v.tail != 0 {
+			// set next (previous) format clearing current one
+			format = append(format, esc...)
+			format = v.tail.appendNos(format, true)
+			format = append(format, 'm')
+		} else {
+			format = append(format, clear...) // just clear
+		}
+	}
+	fmt.Fprintf(s, string(format), v.Value.Value())
+}
+
+func sprintf(format interface{}, args ...interface{}) string {
 	switch ft := format.(type) {
 	case string:
 		return fmt.Sprintf(ft, args...)
 	case Value:
 		for i, v := range args {
 			if val, ok := v.(Value); ok {
-				args[i] = val.setTail(ft.Color())
+				args[i] = &tailedValue{Value: val, tail: ft.Color()}
 				continue
 			}
 		}
diff --git a/sprintf_test.go b/sprintf_test.go
index ee9a08f..370662f 100644
--- a/sprintf_test.go
+++ b/sprintf_test.go
@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2016-2020 The Aurora Authors. All rights reserved.
+// Copyright (c) 2016-2022 The Aurora Authors. All rights reserved.
 // This program is free software. It comes without any warranty,
 // to the extent permitted by applicable law. You can redistribute
 // it and/or modify it under the terms of the Unlicense. See LICENSE
@@ -37,19 +37,52 @@ package aurora
 
 import (
 	"testing"
+
+	"github.com/stretchr/testify/assert"
 )
 
 type noString string
 
 func Test_Sprintf(t *testing.T) {
-	//b := Black("x")
-	v := Sprintf(noString("delta: +%d"), 3)
-	if v != "delta: +3" {
-		t.Error("Sprintf: wrong result")
-	}
-	v = Sprintf(Red("deltas: +%d, %d, %d points"), 3, 5, 9)
-	want := "\033[31mdeltas: +3, 5, 9 points\033[0m"
-	if v != want {
-		t.Errorf("Sprintf: want: %q, got %q", want, v)
-	}
+	var want, got string
+
+	want = "delta: +3"
+	got = Sprintf(noString("delta: +%d"), 3)
+	assert.Equal(t, want, got)
+
+	want = "\033[31mdeltas: +3, 5, 9 points\033[0m"
+	got = Sprintf(Red("deltas: +%d, %d, %d points"), 3, 5, 9)
+	assert.Equal(t, want, got)
+
+	// %s
+	want = `quoted:     "blue"` +
+		`,      green`
+	got = Sprintf(Red("quoted: % 10q, % 10s"), Blue("blue"), Green("green"))
+	assert.Equal(t, want, got)
+
+	// on string
+	want = `quoted: blue, green`
+	got = Sprintf("quoted: %s, %s", Blue("blue"), Green("green"))
+	assert.Equal(t, want, got)
+
+	// no tail
+	want = `quoted: blue, green`
+	got = Sprintf(Clear("quoted: %s, %s"), Blue("blue"), Green("green"))
+	assert.Equal(t, want, got)
+
+	// precision
+	want = `value: 2.78`
+	got = Sprintf(Red("value: %1.2f"), Blue(2.7834))
+	assert.Equal(t, want, got)
+
+	// wide verb
+	want = `%!世(float64=+2.78)`
+	got = Sprintf(Red("%+1.3世"), Blue(2.7834))
+	assert.Equal(t, want, got)
+
+	// decolor
+	var au = New(WithColors(false), WithHyperlinks(false))
+	want = `+2.783`
+	got = au.Sprintf(Red("%+1.3f"), Blue(2.7834))
+	assert.Equal(t, want, got)
 }
diff --git a/value.go b/value.go
index feda25a..9b5f4f0 100644
--- a/value.go
+++ b/value.go
@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2016-2020 The Aurora Authors. All rights reserved.
+// Copyright (c) 2016-2022 The Aurora Authors. All rights reserved.
 // This program is free software. It comes without any warranty,
 // to the extent permitted by applicable law. You can redistribute
 // it and/or modify it under the terms of the Unlicense. See LICENSE
@@ -41,362 +41,14 @@ import (
 	"unicode/utf8"
 )
 
-// A Value represents any printable value
-// with it's color
-type Value interface {
-	// String returns string with colors. If there are any color
-	// or format the string will be terminated with \033[0m
-	fmt.Stringer
-	// Format implements fmt.Formatter interface
-	fmt.Formatter
-	// Color returns value's color
-	Color() Color
-	// Value returns value's value (welcome to the tautology club)
-	Value() interface{}
-
-	//  internals
-	tail() Color
-	setTail(Color) Value
-
-	// Bleach returns copy of original value without colors
-	//
-	// Deprecated: use Reset instead.
-	Bleach() Value
-	// Reset colors and formats
-	Reset() Value
-
-	//
-	// Formats
-	//
-	//
-	// Bold or increased intensity (1).
-	Bold() Value
-	// Faint, decreased intensity, reset the Bold (2).
-	Faint() Value
-	//
-	// DoublyUnderline or Bold off, double-underline
-	// per ECMA-48 (21). It depends.
-	DoublyUnderline() Value
-	// Fraktur, rarely supported (20).
-	Fraktur() Value
-	//
-	// Italic, not widely supported, sometimes
-	// treated as inverse (3).
-	Italic() Value
-	// Underline (4).
-	Underline() Value
-	//
-	// SlowBlink, blinking less than 150
-	// per minute (5).
-	SlowBlink() Value
-	// RapidBlink, blinking 150+ per minute,
-	// not widely supported (6).
-	RapidBlink() Value
-	// Blink is alias for the SlowBlink.
-	Blink() Value
-	//
-	// Reverse video, swap foreground and
-	// background colors (7).
-	Reverse() Value
-	// Inverse is alias for the Reverse
-	Inverse() Value
-	//
-	// Conceal, hidden, not widely supported (8).
-	Conceal() Value
-	// Hidden is alias for the Conceal
-	Hidden() Value
-	//
-	// CrossedOut, characters legible, but
-	// marked for deletion (9).
-	CrossedOut() Value
-	// StrikeThrough is alias for the CrossedOut.
-	StrikeThrough() Value
-	//
-	// Framed (51).
-	Framed() Value
-	// Encircled (52).
-	Encircled() Value
-	//
-	// Overlined (53).
-	Overlined() Value
-
-	//
-	// Foreground colors
-	//
-	//
-	// Black foreground color (30)
-	Black() Value
-	// Red foreground color (31)
-	Red() Value
-	// Green foreground color (32)
-	Green() Value
-	// Yellow foreground color (33)
-	Yellow() Value
-	// Brown foreground color (33)
-	//
-	// Deprecated: use Yellow instead, following specification
-	Brown() Value
-	// Blue foreground color (34)
-	Blue() Value
-	// Magenta foreground color (35)
-	Magenta() Value
-	// Cyan foreground color (36)
-	Cyan() Value
-	// White foreground color (37)
-	White() Value
-	//
-	// Bright foreground colors
-	//
-	// BrightBlack foreground color (90)
-	BrightBlack() Value
-	// BrightRed foreground color (91)
-	BrightRed() Value
-	// BrightGreen foreground color (92)
-	BrightGreen() Value
-	// BrightYellow foreground color (93)
-	BrightYellow() Value
-	// BrightBlue foreground color (94)
-	BrightBlue() Value
-	// BrightMagenta foreground color (95)
-	BrightMagenta() Value
-	// BrightCyan foreground color (96)
-	BrightCyan() Value
-	// BrightWhite foreground color (97)
-	BrightWhite() Value
-	//
-	// Other
-	//
-	// Index of pre-defined 8-bit foreground color
-	// from 0 to 255 (38;5;n).
-	//
-	//       0-  7:  standard colors (as in ESC [ 30–37 m)
-	//       8- 15:  high intensity colors (as in ESC [ 90–97 m)
-	//      16-231:  6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
-	//     232-255:  grayscale from black to white in 24 steps
-	//
-	Index(n uint8) Value
-	// Gray from 0 to 24.
-	Gray(n uint8) Value
-
-	//
-	// Background colors
-	//
-	//
-	// BgBlack background color (40)
-	BgBlack() Value
-	// BgRed background color (41)
-	BgRed() Value
-	// BgGreen background color (42)
-	BgGreen() Value
-	// BgYellow background color (43)
-	BgYellow() Value
-	// BgBrown background color (43)
-	//
-	// Deprecated: use BgYellow instead, following specification
-	BgBrown() Value
-	// BgBlue background color (44)
-	BgBlue() Value
-	// BgMagenta background color (45)
-	BgMagenta() Value
-	// BgCyan background color (46)
-	BgCyan() Value
-	// BgWhite background color (47)
-	BgWhite() Value
-	//
-	// Bright background colors
-	//
-	// BgBrightBlack background color (100)
-	BgBrightBlack() Value
-	// BgBrightRed background color (101)
-	BgBrightRed() Value
-	// BgBrightGreen background color (102)
-	BgBrightGreen() Value
-	// BgBrightYellow background color (103)
-	BgBrightYellow() Value
-	// BgBrightBlue background color (104)
-	BgBrightBlue() Value
-	// BgBrightMagenta background color (105)
-	BgBrightMagenta() Value
-	// BgBrightCyan background color (106)
-	BgBrightCyan() Value
-	// BgBrightWhite background color (107)
-	BgBrightWhite() Value
-	//
-	// Other
-	//
-	// BgIndex of 8-bit pre-defined background color
-	// from 0 to 255 (48;5;n).
-	//
-	//       0-  7:  standard colors (as in ESC [ 40–47 m)
-	//       8- 15:  high intensity colors (as in ESC [100–107 m)
-	//      16-231:  6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
-	//     232-255:  grayscale from black to white in 24 steps
-	//
-	BgIndex(n uint8) Value
-	// BgGray from 0 to 24.
-	BgGray(n uint8) Value
-
-	//
-	// Special
-	//
-	// Colorize removes existing colors and
-	// formats of the argument and applies given.
-	Colorize(color Color) Value
-}
-
-// Value without colors
-
-type valueClear struct {
-	value interface{}
-}
-
-func (vc valueClear) String() string { return fmt.Sprint(vc.value) }
-
-func (vc valueClear) Color() Color       { return 0 }
-func (vc valueClear) Value() interface{} { return vc.value }
-
-func (vc valueClear) tail() Color         { return 0 }
-func (vc valueClear) setTail(Color) Value { return vc }
-
-func (vc valueClear) Bleach() Value { return vc }
-func (vc valueClear) Reset() Value  { return vc }
-
-func (vc valueClear) Bold() Value            { return vc }
-func (vc valueClear) Faint() Value           { return vc }
-func (vc valueClear) DoublyUnderline() Value { return vc }
-func (vc valueClear) Fraktur() Value         { return vc }
-func (vc valueClear) Italic() Value          { return vc }
-func (vc valueClear) Underline() Value       { return vc }
-func (vc valueClear) SlowBlink() Value       { return vc }
-func (vc valueClear) RapidBlink() Value      { return vc }
-func (vc valueClear) Blink() Value           { return vc }
-func (vc valueClear) Reverse() Value         { return vc }
-func (vc valueClear) Inverse() Value         { return vc }
-func (vc valueClear) Conceal() Value         { return vc }
-func (vc valueClear) Hidden() Value          { return vc }
-func (vc valueClear) CrossedOut() Value      { return vc }
-func (vc valueClear) StrikeThrough() Value   { return vc }
-func (vc valueClear) Framed() Value          { return vc }
-func (vc valueClear) Encircled() Value       { return vc }
-func (vc valueClear) Overlined() Value       { return vc }
-
-func (vc valueClear) Black() Value         { return vc }
-func (vc valueClear) Red() Value           { return vc }
-func (vc valueClear) Green() Value         { return vc }
-func (vc valueClear) Yellow() Value        { return vc }
-func (vc valueClear) Brown() Value         { return vc }
-func (vc valueClear) Blue() Value          { return vc }
-func (vc valueClear) Magenta() Value       { return vc }
-func (vc valueClear) Cyan() Value          { return vc }
-func (vc valueClear) White() Value         { return vc }
-func (vc valueClear) BrightBlack() Value   { return vc }
-func (vc valueClear) BrightRed() Value     { return vc }
-func (vc valueClear) BrightGreen() Value   { return vc }
-func (vc valueClear) BrightYellow() Value  { return vc }
-func (vc valueClear) BrightBlue() Value    { return vc }
-func (vc valueClear) BrightMagenta() Value { return vc }
-func (vc valueClear) BrightCyan() Value    { return vc }
-func (vc valueClear) BrightWhite() Value   { return vc }
-func (vc valueClear) Index(uint8) Value    { return vc }
-func (vc valueClear) Gray(uint8) Value     { return vc }
-
-func (vc valueClear) BgBlack() Value         { return vc }
-func (vc valueClear) BgRed() Value           { return vc }
-func (vc valueClear) BgGreen() Value         { return vc }
-func (vc valueClear) BgYellow() Value        { return vc }
-func (vc valueClear) BgBrown() Value         { return vc }
-func (vc valueClear) BgBlue() Value          { return vc }
-func (vc valueClear) BgMagenta() Value       { return vc }
-func (vc valueClear) BgCyan() Value          { return vc }
-func (vc valueClear) BgWhite() Value         { return vc }
-func (vc valueClear) BgBrightBlack() Value   { return vc }
-func (vc valueClear) BgBrightRed() Value     { return vc }
-func (vc valueClear) BgBrightGreen() Value   { return vc }
-func (vc valueClear) BgBrightYellow() Value  { return vc }
-func (vc valueClear) BgBrightBlue() Value    { return vc }
-func (vc valueClear) BgBrightMagenta() Value { return vc }
-func (vc valueClear) BgBrightCyan() Value    { return vc }
-func (vc valueClear) BgBrightWhite() Value   { return vc }
-func (vc valueClear) BgIndex(uint8) Value    { return vc }
-func (vc valueClear) BgGray(uint8) Value     { return vc }
-func (vc valueClear) Colorize(Color) Value   { return vc }
-
-func (vc valueClear) Format(s fmt.State, verb rune) {
-	// it's enough for many cases (%-+020.10f)
-	// %          - 1
-	// availFlags - 3 (5)
-	// width      - 2
-	// prec       - 3 (.23)
-	// verb       - 1
-	// --------------
-	//             10
-	format := make([]byte, 1, 10)
-	format[0] = '%'
-	var f byte
-	for i := 0; i < len(availFlags); i++ {
-		if f = availFlags[i]; s.Flag(int(f)) {
-			format = append(format, f)
-		}
-	}
-	var width, prec int
-	var ok bool
-	if width, ok = s.Width(); ok {
-		format = strconv.AppendInt(format, int64(width), 10)
-	}
-	if prec, ok = s.Precision(); ok {
-		format = append(format, '.')
-		format = strconv.AppendInt(format, int64(prec), 10)
-	}
-	if verb > utf8.RuneSelf {
-		format = append(format, string(verb)...)
-	} else {
-		format = append(format, byte(verb))
-	}
-	fmt.Fprintf(s, string(format), vc.value)
-}
-
-// Value within colors
-
-type value struct {
-	value     interface{} // value as it
-	color     Color       // this color
-	tailColor Color       // tail color
-}
-
-func (v value) String() string {
-	if v.color != 0 {
-		if v.tailColor != 0 {
-			return esc + v.color.Nos(true) + "m" +
-				fmt.Sprint(v.value) +
-				esc + v.tailColor.Nos(true) + "m"
-		}
-		return esc + v.color.Nos(false) + "m" + fmt.Sprint(v.value) + clear
-	}
-	return fmt.Sprint(v.value)
-}
-
-func (v value) Color() Color { return v.color }
-
-func (v value) Bleach() Value {
-	v.color, v.tailColor = 0, 0
-	return v
-}
-
-func (v value) Reset() Value {
-	v.color, v.tailColor = 0, 0
-	return v
-}
-
-func (v value) tail() Color { return v.tailColor }
-
-func (v value) setTail(t Color) Value {
-	v.tailColor = t
-	return v
-}
-
-func (v value) Value() interface{} { return v.value }
+// compile-time check
+var (
+	_ fmt.Stringer  = Value{}
+	_ fmt.Formatter = Value{}
+	_ Colored       = Value{}
+)
 
-func (v value) Format(s fmt.State, verb rune) {
+func coloredFormat(color Color, s fmt.State, verb rune) string {
 
 	// it's enough for many cases (%-+020.10f)
 	// %          - 1
@@ -419,327 +71,567 @@ func (v value) Format(s fmt.State, verb rune) {
 	//
 	// 10 + 59 * 2 = 128
 
-	format := make([]byte, 0, 128)
-	if v.color != 0 {
+	var format = make([]byte, 0, 128)
+
+	if color != 0 {
 		format = append(format, esc...)
-		format = v.color.appendNos(format, v.tailColor != 0)
+		format = color.appendNos(format, false)
 		format = append(format, 'm')
 	}
+
 	format = append(format, '%')
+
 	var f byte
 	for i := 0; i < len(availFlags); i++ {
 		if f = availFlags[i]; s.Flag(int(f)) {
 			format = append(format, f)
 		}
 	}
-	var width, prec int
-	var ok bool
+
+	var (
+		width, prec int
+		ok          bool
+	)
 	if width, ok = s.Width(); ok {
 		format = strconv.AppendInt(format, int64(width), 10)
 	}
+
 	if prec, ok = s.Precision(); ok {
 		format = append(format, '.')
 		format = strconv.AppendInt(format, int64(prec), 10)
 	}
+
 	if verb > utf8.RuneSelf {
 		format = append(format, string(verb)...)
 	} else {
 		format = append(format, byte(verb))
 	}
-	if v.color != 0 {
-		if v.tailColor != 0 {
-			// set next (previous) format clearing current one
-			format = append(format, esc...)
-			format = v.tailColor.appendNos(format, true)
-			format = append(format, 'm')
+
+	if color != 0 {
+		format = append(format, clear...) // just clear
+	}
+
+	return string(format)
+}
+
+type colorConfig uint64
+
+const (
+	colorPin      colorConfig = 1 << 32
+	hyperlinksPin colorConfig = 1 << 33
+)
+
+func (cc colorConfig) colorsEnabled() bool {
+	return cc&colorPin != 0
+}
+
+func (cc colorConfig) hyperlinksEnbaled() bool {
+	return cc&hyperlinksPin != 0
+}
+
+func (cc colorConfig) color() Color {
+	if cc.colorsEnabled() {
+		return Color(uint32(cc)) // lower 32 bits only
+	}
+	return 0 // even if a color set
+}
+
+func (cc colorConfig) resetColor() colorConfig {
+	return cc & (colorPin | hyperlinksPin)
+}
+
+// A Value represents any printable value
+// with or without colors, formats and a link.
+type Value struct {
+	value     interface{} // value as is
+	cc        colorConfig // color & config
+	hyperlink *hyperlink  // hyperlink target and parameters
+}
+
+// String implements standard fmt.Stringer interface.
+func (v Value) String() string {
+	var (
+		t     []byte
+		val   = fmt.Sprint(v.value)
+		color = v.cc.color()
+	)
+
+	if v.cc.hyperlinksEnbaled() && v.hyperlink.isExists() {
+		var (
+			ln  = len(val)
+			nos string
+		)
+		// calculate length
+		ln += v.hyperlink.headLen()
+		if color != 0 {
+			ln += len(esc)
+			nos = color.Nos(false)
+			ln += len(nos) + len("m")
+			ln += len(clear)
+		}
+		ln += v.hyperlink.tailLen()
+		// fill
+		t = make([]byte, 0, ln)
+		t = append(t, v.hyperlink.headBytes()...)
+		if color != 0 {
+			t = append(t, esc...)
+			t = append(t, nos...)
+			t = append(t, 'm')
+			t = append(t, val...)
+			t = append(t, clear...)
 		} else {
-			format = append(format, clear...) // just clear
+			t = append(t, val...)
 		}
+		t = append(t, v.hyperlink.tailBytes()...)
+		return string(t)
+	}
+
+	// no links, only colors & formats
+	if color != 0 {
+		return esc + color.Nos(false) + "m" + val + clear
+	}
+
+	// no links, no colors, no formats, just the value
+	return val
+}
+
+// Color returns colors and formats of the Value.
+func (v Value) Color() Color {
+	return v.cc.color()
+}
+
+// Reset colors, formats and links.
+func (v Value) Reset() Value {
+	v.cc, v.hyperlink = v.cc.resetColor(), nil
+	return v
+}
+
+// Clear colors and formats, preserving links.
+func (v Value) Clear() Value {
+	v.cc = v.cc.resetColor()
+	return v
+}
+
+// Value returns value's value (welcome to the tautology club)
+func (v Value) Value() interface{} {
+	return v.value
+}
+
+// Format implements standard fmt.Formatter interface.
+func (v Value) Format(s fmt.State, verb rune) {
+	if !v.cc.hyperlinksEnbaled() {
+		fmt.Fprintf(s, coloredFormat(v.Color(), s, verb), v.value)
+		return
 	}
-	fmt.Fprintf(s, string(format), v.value)
+	v.hyperlink.writeHead(s)
+	fmt.Fprintf(s, coloredFormat(v.Color(), s, verb), v.value)
+	v.hyperlink.writeTail(s)
 }
 
-func (v value) Bold() Value {
-	v.color = (v.color &^ FaintFm) | BoldFm
+// Formats
+//
+// Bold or increased intensity (1).
+func (v Value) Bold() Value {
+	v.cc = colorConfig(v.cc.color().Bold()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) Faint() Value {
-	v.color = (v.color &^ BoldFm) | FaintFm
+// Faint, decreased intensity, reset the Bold (2).
+func (v Value) Faint() Value {
+	v.cc = colorConfig(v.cc.color().Faint()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) DoublyUnderline() Value {
-	v.color |= DoublyUnderlineFm
+// DoublyUnderline or Bold off, double-underline per ECMA-48 (21). It depends.
+func (v Value) DoublyUnderline() Value {
+	v.cc = colorConfig(v.cc.color().DoublyUnderline()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) Fraktur() Value {
-	v.color |= FrakturFm
+// Fraktur, rarely supported (20).
+func (v Value) Fraktur() Value {
+	v.cc = colorConfig(v.cc.color().Fraktur()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) Italic() Value {
-	v.color |= ItalicFm
+// Italic, not widely supported, sometimes treated as inverse (3).
+func (v Value) Italic() Value {
+	v.cc = colorConfig(v.cc.color().Italic()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) Underline() Value {
-	v.color |= UnderlineFm
+// Underline (4).
+func (v Value) Underline() Value {
+	v.cc = colorConfig(v.cc.color().Underline()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) SlowBlink() Value {
-	v.color = (v.color &^ RapidBlinkFm) | SlowBlinkFm
+// SlowBlink, blinking less than 150 per minute (5).
+func (v Value) SlowBlink() Value {
+	v.cc = colorConfig(v.cc.color().SlowBlink()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) RapidBlink() Value {
-	v.color = (v.color &^ SlowBlinkFm) | RapidBlinkFm
+// RapidBlink, blinking 150+ per minute, not widely supported (6).
+func (v Value) RapidBlink() Value {
+	v.cc = colorConfig(v.cc.color().RapidBlink()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) Blink() Value {
+// Blink is alias for the SlowBlink.
+func (v Value) Blink() Value {
 	return v.SlowBlink()
 }
 
-func (v value) Reverse() Value {
-	v.color |= ReverseFm
+// Reverse video, swap foreground and background colors (7).
+func (v Value) Reverse() Value {
+	v.cc = colorConfig(v.cc.color().Reverse()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) Inverse() Value {
+// Inverse is alias for the Reverse.
+func (v Value) Inverse() Value {
 	return v.Reverse()
 }
 
-func (v value) Conceal() Value {
-	v.color |= ConcealFm
+// Conceal, hidden, not widely supported (8).
+func (v Value) Conceal() Value {
+	v.cc = colorConfig(v.cc.color().Conceal()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) Hidden() Value {
+// Hidden is alias for the Conceal.
+func (v Value) Hidden() Value {
 	return v.Conceal()
 }
 
-func (v value) CrossedOut() Value {
-	v.color |= CrossedOutFm
+// CrossedOut, characters legible, but marked for deletion (9).
+func (v Value) CrossedOut() Value {
+	v.cc = colorConfig(v.cc.color().CrossedOut()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) StrikeThrough() Value {
+// StrikeThrough is alias for the CrossedOut.
+func (v Value) StrikeThrough() Value {
 	return v.CrossedOut()
 }
 
-func (v value) Framed() Value {
-	v.color |= FramedFm
+// Framed (51).
+func (v Value) Framed() Value {
+	v.cc = colorConfig(v.cc.color().Framed()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) Encircled() Value {
-	v.color |= EncircledFm
+// Encircled (52).
+func (v Value) Encircled() Value {
+	v.cc = colorConfig(v.cc.color().Encircled()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) Overlined() Value {
-	v.color |= OverlinedFm
+// Overlined (53).
+func (v Value) Overlined() Value {
+	v.cc = colorConfig(v.cc.color().Overlined()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) Black() Value {
-	v.color = (v.color &^ maskFg) | BlackFg
+// Foreground colors.
+//
+// Black foreground color (30).
+func (v Value) Black() Value {
+	v.cc = colorConfig(v.cc.color().Black()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) Red() Value {
-	v.color = (v.color &^ maskFg) | RedFg
+// Red foreground color (31).
+func (v Value) Red() Value {
+	v.cc = colorConfig(v.cc.color().Red()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) Green() Value {
-	v.color = (v.color &^ maskFg) | GreenFg
+// Green foreground color (32).
+func (v Value) Green() Value {
+	v.cc = colorConfig(v.cc.color().Green()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) Yellow() Value {
-	v.color = (v.color &^ maskFg) | YellowFg
+// Yellow foreground color (33).
+func (v Value) Yellow() Value {
+	v.cc = colorConfig(v.cc.color().Yellow()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) Brown() Value {
-	return v.Yellow()
+// Blue foreground color (34).
+func (v Value) Blue() Value {
+	v.cc = colorConfig(v.cc.color().Blue()) | v.cc.resetColor()
+	return v
 }
 
-func (v value) Blue() Value {
-	v.color = (v.color &^ maskFg) | BlueFg
+// Magenta foreground color (35).
+func (v Value) Magenta() Value {
+	v.cc = colorConfig(v.cc.color().Magenta()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) Magenta() Value {
-	v.color = (v.color &^ maskFg) | MagentaFg
+// Cyan foreground color (36).
+func (v Value) Cyan() Value {
+	v.cc = colorConfig(v.cc.color().Cyan()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) Cyan() Value {
-	v.color = (v.color &^ maskFg) | CyanFg
+// White foreground color (37).
+func (v Value) White() Value {
+	v.cc = colorConfig(v.cc.color().White()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) White() Value {
-	v.color = (v.color &^ maskFg) | WhiteFg
+// Bright foreground colors.
+//
+// BrightBlack foreground color (90).
+func (v Value) BrightBlack() Value {
+	v.cc = colorConfig(v.cc.color().BrightBlack()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) BrightBlack() Value {
-	v.color = (v.color &^ maskFg) | BrightFg | BlackFg
+// BrightRed foreground color (91).
+func (v Value) BrightRed() Value {
+	v.cc = colorConfig(v.cc.color().BrightRed()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) BrightRed() Value {
-	v.color = (v.color &^ maskFg) | BrightFg | RedFg
+// BrightGreen foreground color (92).
+func (v Value) BrightGreen() Value {
+	v.cc = colorConfig(v.cc.color().BrightGreen()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) BrightGreen() Value {
-	v.color = (v.color &^ maskFg) | BrightFg | GreenFg
+// BrightYellow foreground color (93).
+func (v Value) BrightYellow() Value {
+	v.cc = colorConfig(v.cc.color().BrightYellow()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) BrightYellow() Value {
-	v.color = (v.color &^ maskFg) | BrightFg | YellowFg
+// BrightBlue foreground color (94).
+func (v Value) BrightBlue() Value {
+	v.cc = colorConfig(v.cc.color().BrightBlue()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) BrightBlue() Value {
-	v.color = (v.color &^ maskFg) | BrightFg | BlueFg
+// BrightMagenta foreground color (95).
+func (v Value) BrightMagenta() Value {
+	v.cc = colorConfig(v.cc.color().BrightMagenta()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) BrightMagenta() Value {
-	v.color = (v.color &^ maskFg) | BrightFg | MagentaFg
+// BrightCyan foreground color (96).
+func (v Value) BrightCyan() Value {
+	v.cc = colorConfig(v.cc.color().BrightCyan()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) BrightCyan() Value {
-	v.color = (v.color &^ maskFg) | BrightFg | CyanFg
+// BrightWhite foreground color (97).
+func (v Value) BrightWhite() Value {
+	v.cc = colorConfig(v.cc.color().BrightWhite()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) BrightWhite() Value {
-	v.color = (v.color &^ maskFg) | BrightFg | WhiteFg
+// Other colors.
+//
+// Index of pre-defined 8-bit foreground color from 0 to 255 (38;5;n).
+//
+//	  0-  7:  standard colors (as in ESC [ 30–37 m)
+//	  8- 15:  high intensity colors (as in ESC [ 90–97 m)
+//	 16-231:  6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
+//	232-255:  grayscale from black to white in 24 steps
+func (v Value) Index(n ColorIndex) Value {
+	v.cc = colorConfig(v.cc.color().Index(n)) | v.cc.resetColor()
 	return v
 }
 
-func (v value) Index(n uint8) Value {
-	v.color = (v.color &^ maskFg) | (Color(n) << shiftFg) | flagFg
+// Gray from 0 to 24.
+func (v Value) Gray(n GrayIndex) Value {
+	v.cc = colorConfig(v.cc.color().Gray(n)) | v.cc.resetColor()
 	return v
 }
 
-func (v value) Gray(n uint8) Value {
-	if n > 23 {
-		n = 23
-	}
-	v.color = (v.color &^ maskFg) | (Color(232+n) << shiftFg) | flagFg
+// Background colors
+//
+// BgBlack background color (40).
+func (v Value) BgBlack() Value {
+	v.cc = colorConfig(v.cc.color().BgBlack()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) BgBlack() Value {
-	v.color = (v.color &^ maskBg) | BlackBg
+// BgRed background color (41).
+func (v Value) BgRed() Value {
+	v.cc = colorConfig(v.cc.color().BgRed()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) BgRed() Value {
-	v.color = (v.color &^ maskBg) | RedBg
+// BgGreen background color (42).
+func (v Value) BgGreen() Value {
+	v.cc = colorConfig(v.cc.color().BgGreen()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) BgGreen() Value {
-	v.color = (v.color &^ maskBg) | GreenBg
+// BgYellow background color (43).
+func (v Value) BgYellow() Value {
+	v.cc = colorConfig(v.cc.color().BgYellow()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) BgYellow() Value {
-	v.color = (v.color &^ maskBg) | YellowBg
+// BgBlue background color (44).
+func (v Value) BgBlue() Value {
+	v.cc = colorConfig(v.cc.color().BgBlue()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) BgBrown() Value {
-	return v.BgYellow()
+// BgMagenta background color (45).
+func (v Value) BgMagenta() Value {
+	v.cc = colorConfig(v.cc.color().BgMagenta()) | v.cc.resetColor()
+	return v
 }
 
-func (v value) BgBlue() Value {
-	v.color = (v.color &^ maskBg) | BlueBg
+// BgCyan background color (46).
+func (v Value) BgCyan() Value {
+	v.cc = colorConfig(v.cc.color().BgCyan()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) BgMagenta() Value {
-	v.color = (v.color &^ maskBg) | MagentaBg
+// BgWhite background color (47).
+func (v Value) BgWhite() Value {
+	v.cc = colorConfig(v.cc.color().BgWhite()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) BgCyan() Value {
-	v.color = (v.color &^ maskBg) | CyanBg
+// Bright background colors.
+//
+// BgBrightBlack background color (100).
+func (v Value) BgBrightBlack() Value {
+	v.cc = colorConfig(v.cc.color().BgBrightBlack()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) BgWhite() Value {
-	v.color = (v.color &^ maskBg) | WhiteBg
+// BgBrightRed background color (101).
+func (v Value) BgBrightRed() Value {
+	v.cc = colorConfig(v.cc.color().BgBrightRed()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) BgBrightBlack() Value {
-	v.color = (v.color &^ maskBg) | BrightBg | BlackBg
+// BgBrightGreen background color (102).
+func (v Value) BgBrightGreen() Value {
+	v.cc = colorConfig(v.cc.color().BgBrightGreen()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) BgBrightRed() Value {
-	v.color = (v.color &^ maskBg) | BrightBg | RedBg
+// BgBrightYellow background color (103).
+func (v Value) BgBrightYellow() Value {
+	v.cc = colorConfig(v.cc.color().BgBrightYellow()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) BgBrightGreen() Value {
-	v.color = (v.color &^ maskBg) | BrightBg | GreenBg
+// BgBrightBlue background color (104).
+func (v Value) BgBrightBlue() Value {
+	v.cc = colorConfig(v.cc.color().BgBrightBlue()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) BgBrightYellow() Value {
-	v.color = (v.color &^ maskBg) | BrightBg | YellowBg
+// BgBrightMagenta background color (105).
+func (v Value) BgBrightMagenta() Value {
+	v.cc = colorConfig(v.cc.color().BgBrightMagenta()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) BgBrightBlue() Value {
-	v.color = (v.color &^ maskBg) | BrightBg | BlueBg
+// BgBrightCyan background color (106).
+func (v Value) BgBrightCyan() Value {
+	v.cc = colorConfig(v.cc.color().BgBrightCyan()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) BgBrightMagenta() Value {
-	v.color = (v.color &^ maskBg) | BrightBg | MagentaBg
+// BgBrightWhite background color (107).
+func (v Value) BgBrightWhite() Value {
+	v.cc = colorConfig(v.cc.color().BgBrightWhite()) | v.cc.resetColor()
 	return v
 }
 
-func (v value) BgBrightCyan() Value {
-	v.color = (v.color &^ maskBg) | BrightBg | CyanBg
+// Other background colors.
+//
+// BgIndex of 8-bit pre-defined background color from 0 to 255 (48;5;n).
+//
+//	  0-  7:  standard colors (as in ESC [ 40–47 m)
+//	  8- 15:  high intensity colors (as in ESC [100–107 m)
+//	 16-231:  6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
+//	232-255:  grayscale from black to white in 24 steps
+func (v Value) BgIndex(n ColorIndex) Value {
+	v.cc = colorConfig(v.cc.color().BgIndex(n)) | v.cc.resetColor()
 	return v
 }
 
-func (v value) BgBrightWhite() Value {
-	v.color = (v.color &^ maskBg) | BrightBg | WhiteBg
+// BgGray from 0 to 24.
+func (v Value) BgGray(n GrayIndex) Value {
+	v.cc = colorConfig(v.cc.color().BgGray(n)) | v.cc.resetColor()
 	return v
 }
 
-func (v value) BgIndex(n uint8) Value {
-	v.color = (v.color &^ maskBg) | (Color(n) << shiftBg) | flagBg
+// Special colorization method.
+//
+// Colorize removes existing colors and formats of the argument and applies
+// given.
+func (v Value) Colorize(color Color) Value {
+	v.cc = colorConfig(color) | v.cc.resetColor()
 	return v
 }
 
-func (v value) BgGray(n uint8) Value {
-	if n > 23 {
-		n = 23
+// Hyperlinks feature
+//
+// Hyperlink with given target and parameters. If hyperlinks feature is
+// disabled, then the 'arg' argument dropped and the 'target' used instead,
+// inheriting all colors and format from the 'arg' (if it's a Colored).
+//
+// See https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
+// for details about the hyperlinks feature.
+//
+// The Hyperlink doesn't escape the target and the parameters. They should be
+// checked and escaped before. See HyperlinkEscape function.
+//
+// See also HyperlinkID function.
+//
+// For a simple example
+//
+//	val.Hyperlink("http://example.com")
+//
+// and an example with ID
+//
+//	val.Hyperlink("http://example.com", aurora.HyperlinkID("10"))
+//
+// Successive calls replace previously set target and parameters.
+func (v Value) Hyperlink(target string, params ...HyperlinkParam) Value {
+	if !v.cc.hyperlinksEnbaled() {
+		v.value = target // drop value, use the target
+		v.hyperlink = &hyperlink{
+			target: target, // keep for the HyperlinkTarget method
+		}
+		return v
 	}
-	v.color = (v.color &^ maskBg) | (Color(232+n) << shiftBg) | flagBg
+	if v.hyperlink == nil {
+		v.hyperlink = new(hyperlink)
+	}
+	v.hyperlink.target = target
+	v.hyperlink.params = params
 	return v
 }
 
-func (v value) Colorize(color Color) Value {
-	v.color = color
-	return v
+// HyperlinkTarget if any.
+func (v Value) HyperlinkTarget() (target string) {
+	if v.hyperlink != nil {
+		return v.hyperlink.target
+	}
+	return // nothing
+}
+
+// HyperlinkParams if any.
+func (v Value) HyperlinkParams() (params []HyperlinkParam) {
+	if v.hyperlink != nil {
+		return v.hyperlink.params
+	}
+	return // nil
 }
diff --git a/value_test.go b/value_test.go
index 4553277..5043763 100644
--- a/value_test.go
+++ b/value_test.go
@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2016-2020 The Aurora Authors. All rights reserved.
+// Copyright (c) 2016-2022 The Aurora Authors. All rights reserved.
 // This program is free software. It comes without any warranty,
 // to the extent permitted by applicable law. You can redistribute
 // it and/or modify it under the terms of the Unlicense. See LICENSE
@@ -38,306 +38,340 @@ package aurora
 import (
 	"fmt"
 	"testing"
+
+	"github.com/stretchr/testify/assert"
 )
 
 func TestValue_String(t *testing.T) {
-	var v Value
+	var au *Aurora
 	// colorized
-	v = value{"x", 0, 0}
-	if x := v.String(); x != "x" {
-		t.Errorf("(value).String: want %q, got %q", "x", x)
-	}
-	v = value{"x", BlackFg, RedBg}
-	want := "\033[0;30mx\033[0;41m"
-	if got := v.String(); want != got {
-		t.Errorf("(value).String: want %q, got %q", want, got)
-	}
+	au = New()
+	assert.Equal(t, "x", au.Clear("x").String())
+	assert.Equal(t, "\033[30;41mx\033[0m", au.Black("x").BgRed().String())
 	// clear
-	v = valueClear{"x"}
-	if x := v.String(); x != "x" {
-		t.Errorf("(value).String: want %q, got %q", "x", x)
-	}
-
+	au = New(WithColors(false))
+	assert.Equal(t, "x", au.Black("x").BgRed().String())
+	// colorized hyperlink
+	au = New()
+	assert.Equal(t, `]8;;http://example.com`+
+		`\x]8;;\`,
+		au.Red("x").Hyperlink("http://example.com").String())
+	// clear hyperlink
+	assert.Equal(t, `]8;;http://example.com\x]8;;\`,
+		au.Clear("x").Hyperlink("http://example.com").String())
 }
 
 func TestValue_Color(t *testing.T) {
 	// colorized
-	if (value{"", RedFg, 0}).Color() != RedFg {
-		t.Error("wrong color")
-	}
-	// clear
-	if (valueClear{0}).Color() != 0 {
-		t.Error("wrong color")
-	}
+	assert.Equal(t, RedFg, New().Red("x").Color())
+	// clear, also red
+	assert.Equal(t, RedFg, New().Red("x").Color())
 }
 
 func TestValue_Value(t *testing.T) {
 	// colorized
-	if (value{"x", RedFg, BlueBg}).Value() != "x" {
-		t.Error("wrong value")
-	}
-	// clear
-	if (valueClear{"x"}).Value() != "x" {
-		t.Error("wrong value")
-	}
-}
-
-func TestValue_Bleach(t *testing.T) {
-	// colorized
-	if (value{"x", RedFg, BlueBg}).Bleach() != (value{value: "x"}) {
-		t.Error("wrong bleached")
-	}
+	assert.Equal(t, "x", New().Clear("x").Value())
 	// clear
-	if (valueClear{"x"}).Bleach() != (valueClear{"x"}) {
-		t.Error("wrong bleached")
-	}
+	assert.Equal(t, "x", New(WithColors(false)).Black("x").BgRed().Value())
 }
 
-func TestValue_Format(t *testing.T) {
-	var v Value
-	var want, got string
-	//
+func TestValue_Reset(t *testing.T) {
 	// colorized
-	//
-	v = value{3.14, RedFg, BlueBg}
-	got = fmt.Sprintf("%+1.3g", v)
-	want = "\033[0;31m" + fmt.Sprintf("%+1.3g", 3.14) + "\033[0;44m"
-	if want != got {
-		t.Errorf("Format: want %q, got %q", want, got)
-	}
-	//
-	var utf8Verb = "%+1.3世" // verb that fit more then 1 byte
-	got = fmt.Sprintf(utf8Verb, v)
-	want = "\033[0;31m" + fmt.Sprintf(utf8Verb, 3.14) + "\033[0;44m"
-	if want != got {
-		t.Errorf("Format: want %q, got %q", want, got)
-	}
-	//
+	var au = New()
+	assert.Equal(t, Value{
+		cc:    au.cc,
+		value: "x",
+	}, au.Red("x").BgBlack().Reset())
 	// clear
-	//
-	v = valueClear{3.14}
-	got = fmt.Sprintf("%+1.3g", v)
-	want = fmt.Sprintf("%+1.3g", 3.14)
-	if want != got {
-		t.Errorf("Format: want %q, got %q", want, got)
-	}
-	//
-	got = fmt.Sprintf(utf8Verb, v)
-	want = fmt.Sprintf(utf8Verb, 3.14)
-	if want != got {
-		t.Errorf("Format: want %q, got %q", want, got)
-	}
+	au = New(WithColors(false))
+	assert.Equal(t, Value{
+		cc:    au.cc,
+		value: "x",
+	}, au.Red("x").BgBlack().Reset())
+	// a hyperlink
+	au = New()
+	assert.Equal(t,
+		Value{
+			cc:    au.cc,
+			value: "x",
+		}, au.Red("x").
+			BgBlack().
+			Hyperlink("http://example.com/path", HyperlinkID("10")).
+			Reset())
 }
 
-func Test_tail(t *testing.T) {
+func TestValue_Clear(t *testing.T) {
 	// colorized
-	if (value{"x", 0, BlueBg}).tail() != BlueBg {
-		t.Error("wrong tail color")
-	}
+	var au = New()
+	assert.Equal(t, Value{
+		cc:    au.cc,
+		value: "x",
+	}, au.Red("x").BgBlack().Clear())
 	// clear
-	if (valueClear{"x"}).tail() != 0 {
-		t.Error("wrong tail color")
-	}
+	au = New(WithColors(false))
+	assert.Equal(t, Value{
+		cc:    au.cc,
+		value: "x",
+	}, au.Red("x").BgBlack().Clear())
+	// a hyperlink
+	au = New()
+	assert.EqualValues(t,
+		Value{
+			cc:    au.cc,
+			value: "x",
+			hyperlink: &hyperlink{
+				target: "http://example.com/path",
+				params: []HyperlinkParam{{
+					Key:   HyperlinkIDKey,
+					Value: "10",
+				}},
+			},
+		}, au.Red("x").
+			BgBlack().
+			Hyperlink("http://example.com/path", HyperlinkID("10")).
+			Clear())
 }
 
-func Test_setTail(t *testing.T) {
+func TestValue_Format(t *testing.T) {
 	// colorized
-	if (value{"x", 0, 0}).setTail(RedFg) != (value{"x", 0, RedFg}) {
-		t.Error("wrong setTail behavior")
-	}
+	var au = New()
+	assert.Equal(t, "\033[31;44m"+fmt.Sprintf("%+1.3g", 3.14)+"\033[0m",
+		fmt.Sprintf("%+1.3g", au.Red(3.14).BgBlue()))
+	const utf8Verb = "%+1.3世" // verb that fit more then 1 byte
+	assert.Equal(t, "\033[31;44m"+"%!世(float64=+3.14)"+"\033[0m",
+		fmt.Sprintf(utf8Verb, au.Red(3.14).BgBlue())) //nolint
 	// clear
-	if (valueClear{"x"}).setTail(RedFg) != (valueClear{"x"}) {
-		t.Error("wrong setTail behavior")
-	}
+	au = New(WithColors(false))
+	assert.Equal(t, fmt.Sprintf("%+1.3g", 3.14),
+		fmt.Sprintf("%+1.3g", au.Red(3.14).BgBlue()))
+	assert.Equal(t, "%!世(float64=+3.14)",
+		fmt.Sprintf(utf8Verb, au.Red(3.14).BgBlue())) //nolint
 }
 
 func TestValue_colors(t *testing.T) {
-	test := func(name string, v Value, clr Color) {
+	var test = func(name string, v Value, clr Color) {
 		t.Helper()
-		if c := v.Color(); c != clr {
-			t.Errorf("wrong color for %s: want %d, got %d", name, clr, c)
-		}
+		assert.Equal(t, clr, v.Color())
 	}
 	// colorized
-	test("Reset", Reset("x"), 0)
-	test("Bold", Bold("x"), BoldFm)
-	test("Faint", Faint("x"), FaintFm)
-	test("DoublyUnderline", DoublyUnderline("x"), DoublyUnderlineFm)
-	test("Fraktur & Yellow", Yellow(Fraktur("x")), FrakturFm|YellowFg)
-	test("Italic", Italic("x"), ItalicFm)
-	test("Underline", Underline("x"), UnderlineFm)
-	test("SlowBlink", SlowBlink("x"), SlowBlinkFm)
-	test("RapidBlink", RapidBlink("x"), RapidBlinkFm)
-	test("Blink", Blink("x"), BlinkFm)
-	test("Reverse", Reverse("x"), ReverseFm)
-	test("Inverse", Inverse("x"), InverseFm)
-	test("Conceal", Conceal("x"), ConcealFm)
-	test("Hidden", Hidden("x"), HiddenFm)
-	test("CrossedOut", CrossedOut("x"), CrossedOutFm)
-	test("StrikeThrough", StrikeThrough("x"), StrikeThroughFm)
-	test("Framed", Framed("x"), FramedFm)
-	test("Encircled", Encircled("x"), EncircledFm)
-	test("Overlined", Overlined("x"), OverlinedFm)
-	test("Black", Black("x"), BlackFg)
-	test("Red", Red("x"), RedFg)
-	test("Green", Green("x"), GreenFg)
-	test("Yellow", Yellow("x"), YellowFg)
-	test("Brown", Brown("x"), BrownFg)
-	test("Blue", Blue("x"), BlueFg)
-	test("Magenta", Magenta("x"), MagentaFg)
-	test("Cyan", Cyan("x"), CyanFg)
-	test("White", White("x"), WhiteFg)
-	test("BrightBlack", BrightBlack("x"), BrightFg|BlackFg)
-	test("BrightRed", BrightRed("x"), BrightFg|RedFg)
-	test("BrightGreen", BrightGreen("x"), BrightFg|GreenFg)
-	test("BrightYellow", BrightYellow("x"), BrightFg|YellowFg)
-	test("BrightBlue", BrightBlue("x"), BrightFg|BlueFg)
-	test("BrightMagenta", BrightMagenta("x"), BrightFg|MagentaFg)
-	test("BrightCyan", BrightCyan("x"), BrightFg|CyanFg)
-	test("BrightWhite", BrightWhite("x"), BrightFg|WhiteFg)
-	test("Index", Index(178, "x"), (Color(178)<<shiftFg)|flagFg)
-	test("Gray", Gray(14, "x"), (Color(14+232)<<shiftFg)|flagFg)
-	test("BgBlack", BgBlack("x"), BlackBg)
-	test("BgRed", BgRed("x"), RedBg)
-	test("BgGreen", BgGreen("x"), GreenBg)
-	test("BgYellow", BgYellow("x"), YellowBg)
-	test("BgBrown", BgBrown("x"), BrownBg)
-	test("BgBlue", BgBlue("x"), BlueBg)
-	test("BgMagenta", BgMagenta("x"), MagentaBg)
-	test("BgCyan", BgCyan("x"), CyanBg)
-	test("BgWhite", BgWhite("x"), WhiteBg)
-	test("BgBrightBlack", BgBrightBlack("x"), BrightBg|BlackBg)
-	test("BgBrightRed", BgBrightRed("x"), BrightBg|RedBg)
-	test("BgBrightGreen", BgBrightGreen("x"), BrightBg|GreenBg)
-	test("BgBrightYellow", BgBrightYellow("x"), BrightBg|YellowBg)
-	test("BgBrightBlue", BgBrightBlue("x"), BrightBg|BlueBg)
-	test("BgBrightMagenta", BgBrightMagenta("x"), BrightBg|MagentaBg)
-	test("BgBrightCyan", BgBrightCyan("x"), BrightBg|CyanBg)
-	test("BgBrightWhite", BgBrightWhite("x"), BrightBg|WhiteBg)
-	test("BgIndex", BgIndex(187, "x"), Color(187)<<shiftBg|flagBg)
-	test("BgGray", BgGray(15, "x"), Color(232+15)<<shiftBg|flagBg)
-	test("Colorize", Colorize("x", RedFg|BlueBg|BrightBg|BoldFm),
+	var au = New()
+	test("Reset", au.Reset("x"), 0)
+	test("Clear", au.Clear("x"), 0)
+	test("Bold", au.Bold("x"), BoldFm)
+	test("Faint", au.Faint("x"), FaintFm)
+	test("DoublyUnderline", au.DoublyUnderline("x"), DoublyUnderlineFm)
+	test("Fraktur & au.Yellow", Yellow(Fraktur("x")), FrakturFm|YellowFg)
+	test("Italic", au.Italic("x"), ItalicFm)
+	test("Underline", au.Underline("x"), UnderlineFm)
+	test("SlowBlink", au.SlowBlink("x"), SlowBlinkFm)
+	test("RapidBlink", au.RapidBlink("x"), RapidBlinkFm)
+	test("Blink", au.Blink("x"), BlinkFm)
+	test("Reverse", au.Reverse("x"), ReverseFm)
+	test("Inverse", au.Inverse("x"), InverseFm)
+	test("Conceal", au.Conceal("x"), ConcealFm)
+	test("Hidden", au.Hidden("x"), HiddenFm)
+	test("CrossedOut", au.CrossedOut("x"), CrossedOutFm)
+	test("StrikeThrough", au.StrikeThrough("x"), StrikeThroughFm)
+	test("Framed", au.Framed("x"), FramedFm)
+	test("Encircled", au.Encircled("x"), EncircledFm)
+	test("Overlined", au.Overlined("x"), OverlinedFm)
+	test("Black", au.Black("x"), BlackFg)
+	test("Red", au.Red("x"), RedFg)
+	test("Green", au.Green("x"), GreenFg)
+	test("Yellow", au.Yellow("x"), YellowFg)
+	test("Blue", au.Blue("x"), BlueFg)
+	test("Magenta", au.Magenta("x"), MagentaFg)
+	test("Cyan", au.Cyan("x"), CyanFg)
+	test("White", au.White("x"), WhiteFg)
+	test("BrightBlack", au.BrightBlack("x"), BrightFg|BlackFg)
+	test("BrightRed", au.BrightRed("x"), BrightFg|RedFg)
+	test("BrightGreen", au.BrightGreen("x"), BrightFg|GreenFg)
+	test("BrightYellow", au.BrightYellow("x"), BrightFg|YellowFg)
+	test("BrightBlue", au.BrightBlue("x"), BrightFg|BlueFg)
+	test("BrightMagenta", au.BrightMagenta("x"), BrightFg|MagentaFg)
+	test("BrightCyan", au.BrightCyan("x"), BrightFg|CyanFg)
+	test("BrightWhite", au.BrightWhite("x"), BrightFg|WhiteFg)
+	test("Index", au.Index(178, "x"), (Color(178)<<shiftFg)|flagFg)
+	test("Gray", au.Gray(14, "x"), (Color(14+232)<<shiftFg)|flagFg)
+	test("BgBlack", au.BgBlack("x"), BlackBg)
+	test("BgRed", au.BgRed("x"), RedBg)
+	test("BgGreen", au.BgGreen("x"), GreenBg)
+	test("BgYellow", au.BgYellow("x"), YellowBg)
+	test("BgBlue", au.BgBlue("x"), BlueBg)
+	test("BgMagenta", au.BgMagenta("x"), MagentaBg)
+	test("BgCyan", au.BgCyan("x"), CyanBg)
+	test("BgWhite", au.BgWhite("x"), WhiteBg)
+	test("BgBrightBlack", au.BgBrightBlack("x"), BrightBg|BlackBg)
+	test("BgBrightRed", au.BgBrightRed("x"), BrightBg|RedBg)
+	test("BgBrightGreen", au.BgBrightGreen("x"), BrightBg|GreenBg)
+	test("BgBrightYellow", au.BgBrightYellow("x"), BrightBg|YellowBg)
+	test("BgBrightBlue", au.BgBrightBlue("x"), BrightBg|BlueBg)
+	test("BgBrightMagenta", au.BgBrightMagenta("x"), BrightBg|MagentaBg)
+	test("BgBrightCyan", au.BgBrightCyan("x"), BrightBg|CyanBg)
+	test("BgBrightWhite", au.BgBrightWhite("x"), BrightBg|WhiteBg)
+	test("BgIndex", au.BgIndex(187, "x"), Color(187)<<shiftBg|flagBg)
+	test("BgGray", au.BgGray(15, "x"), Color(232+15)<<shiftBg|flagBg)
+	test("Colorize", au.Colorize("x", RedFg|BlueBg|BrightBg|BoldFm),
 		RedFg|BlueBg|BrightBg|BoldFm)
 	// clear
-	test("Reset", valueClear{"x"}.Reset(), 0)
-	test("Bold", valueClear{"x"}.Bold(), 0)
-	test("Faint", valueClear{"x"}.Faint(), 0)
-	test("DoublyUnderline", valueClear{"x"}.DoublyUnderline(), 0)
-	test("Fraktur & Yellow", valueClear{"x"}.Fraktur(), 0)
-	test("Italic", valueClear{"x"}.Italic(), 0)
-	test("Underline", valueClear{"x"}.Underline(), 0)
-	test("SlowBlink", valueClear{"x"}.SlowBlink(), 0)
-	test("RapidBlink", valueClear{"x"}.RapidBlink(), 0)
-	test("Blink", valueClear{"x"}.Blink(), 0)
-	test("Reverse", valueClear{"x"}.Reverse(), 0)
-	test("Inverse", valueClear{"x"}.Inverse(), 0)
-	test("Conceal", valueClear{"x"}.Conceal(), 0)
-	test("Hidden", valueClear{"x"}.Hidden(), 0)
-	test("CrossedOut", valueClear{"x"}.CrossedOut(), 0)
-	test("StrikeThrough", valueClear{"x"}.StrikeThrough(), 0)
-	test("Framed", valueClear{"x"}.Framed(), 0)
-	test("Encircled", valueClear{"x"}.Encircled(), 0)
-	test("Overlined", valueClear{"x"}.Overlined(), 0)
-	test("Black", valueClear{"x"}.Black(), 0)
-	test("Red", valueClear{"x"}.Red(), 0)
-	test("Green", valueClear{"x"}.Green(), 0)
-	test("Yellow", valueClear{"x"}.Yellow(), 0)
-	test("Brown", valueClear{"x"}.Brown(), 0)
-	test("Blue", valueClear{"x"}.Blue(), 0)
-	test("Magenta", valueClear{"x"}.Magenta(), 0)
-	test("Cyan", valueClear{"x"}.Cyan(), 0)
-	test("White", valueClear{"x"}.White(), 0)
-	test("BrightBlack", valueClear{"x"}.BrightBlack(), 0)
-	test("BrightRed", valueClear{"x"}.BrightRed(), 0)
-	test("BrightGreen", valueClear{"x"}.BrightGreen(), 0)
-	test("BrightYellow", valueClear{"x"}.BrightYellow(), 0)
-	test("BrightBlue", valueClear{"x"}.BrightBlue(), 0)
-	test("BrightMagenta", valueClear{"x"}.BrightMagenta(), 0)
-	test("BrightCyan", valueClear{"x"}.BrightCyan(), 0)
-	test("BrightWhite", valueClear{"x"}.BrightWhite(), 0)
-	test("Index", valueClear{"x"}.Index(178), 0)
-	test("Gray", valueClear{"x"}.Gray(14), 0)
-	test("BgBlack", valueClear{"x"}.BgBlack(), 0)
-	test("BgRed", valueClear{"x"}.BgRed(), 0)
-	test("BgGreen", valueClear{"x"}.BgGreen(), 0)
-	test("BgYellow", valueClear{"x"}.BgYellow(), 0)
-	test("BgBrown", valueClear{"x"}.BgBrown(), 0)
-	test("BgBlue", valueClear{"x"}.BgBlue(), 0)
-	test("BgMagenta", valueClear{"x"}.BgMagenta(), 0)
-	test("BgCyan", valueClear{"x"}.BgCyan(), 0)
-	test("BgWhite", valueClear{"x"}.BgWhite(), 0)
-	test("BgBrightBlack", valueClear{"x"}.BgBrightBlack(), 0)
-	test("BgBrightRed", valueClear{"x"}.BgBrightRed(), 0)
-	test("BgBrightGreen", valueClear{"x"}.BgBrightGreen(), 0)
-	test("BgBrightYellow", valueClear{"x"}.BgBrightYellow(), 0)
-	test("BgBrightBlue", valueClear{"x"}.BgBrightBlue(), 0)
-	test("BgBrightMagenta", valueClear{"x"}.BgBrightMagenta(), 0)
-	test("BgBrightCyan", valueClear{"x"}.BgBrightCyan(), 0)
-	test("BgBrightWhite", valueClear{"x"}.BgBrightWhite(), 0)
-	test("BgIndex", valueClear{"x"}.BgIndex(187), 0)
-	test("BgGray", valueClear{"x"}.BgGray(15), 0)
-	test("Colorize", valueClear{"x"}.Colorize(RedFg|BlueBg|BrightBg|BoldFm), 0)
+	au = New(WithColors(false))
+	test("Reset", au.Clear("x").Reset(), 0)
+	test("Clear", au.Bold("x").Clear(), 0)
+	test("Bold", au.Clear("x").Bold(), 0)
+	test("Faint", au.Clear("x").Faint(), 0)
+	test("DoublyUnderline", au.Clear("x").DoublyUnderline(), 0)
+	test("Fraktur & Yellow", au.Clear("x").Fraktur(), 0)
+	test("Italic", au.Clear("x").Italic(), 0)
+	test("Underline", au.Clear("x").Underline(), 0)
+	test("SlowBlink", au.Clear("x").SlowBlink(), 0)
+	test("RapidBlink", au.Clear("x").RapidBlink(), 0)
+	test("Blink", au.Clear("x").Blink(), 0)
+	test("Reverse", au.Clear("x").Reverse(), 0)
+	test("Inverse", au.Clear("x").Inverse(), 0)
+	test("Conceal", au.Clear("x").Conceal(), 0)
+	test("Hidden", au.Clear("x").Hidden(), 0)
+	test("CrossedOut", au.Clear("x").CrossedOut(), 0)
+	test("StrikeThrough", au.Clear("x").StrikeThrough(), 0)
+	test("Framed", au.Clear("x").Framed(), 0)
+	test("Encircled", au.Clear("x").Encircled(), 0)
+	test("Overlined", au.Clear("x").Overlined(), 0)
+	test("Black", au.Clear("x").Black(), 0)
+	test("Red", au.Clear("x").Red(), 0)
+	test("Green", au.Clear("x").Green(), 0)
+	test("Yellow", au.Clear("x").Yellow(), 0)
+	test("Blue", au.Clear("x").Blue(), 0)
+	test("Magenta", au.Clear("x").Magenta(), 0)
+	test("Cyan", au.Clear("x").Cyan(), 0)
+	test("White", au.Clear("x").White(), 0)
+	test("BrightBlack", au.Clear("x").BrightBlack(), 0)
+	test("BrightRed", au.Clear("x").BrightRed(), 0)
+	test("BrightGreen", au.Clear("x").BrightGreen(), 0)
+	test("BrightYellow", au.Clear("x").BrightYellow(), 0)
+	test("BrightBlue", au.Clear("x").BrightBlue(), 0)
+	test("BrightMagenta", au.Clear("x").BrightMagenta(), 0)
+	test("BrightCyan", au.Clear("x").BrightCyan(), 0)
+	test("BrightWhite", au.Clear("x").BrightWhite(), 0)
+	test("Index", au.Clear("x").Index(178), 0)
+	test("Gray", au.Clear("x").Gray(14), 0)
+	test("BgBlack", au.Clear("x").BgBlack(), 0)
+	test("BgRed", au.Clear("x").BgRed(), 0)
+	test("BgGreen", au.Clear("x").BgGreen(), 0)
+	test("BgYellow", au.Clear("x").BgYellow(), 0)
+	test("BgBlue", au.Clear("x").BgBlue(), 0)
+	test("BgMagenta", au.Clear("x").BgMagenta(), 0)
+	test("BgCyan", au.Clear("x").BgCyan(), 0)
+	test("BgWhite", au.Clear("x").BgWhite(), 0)
+	test("BgBrightBlack", au.Clear("x").BgBrightBlack(), 0)
+	test("BgBrightRed", au.Clear("x").BgBrightRed(), 0)
+	test("BgBrightGreen", au.Clear("x").BgBrightGreen(), 0)
+	test("BgBrightYellow", au.Clear("x").BgBrightYellow(), 0)
+	test("BgBrightBlue", au.Clear("x").BgBrightBlue(), 0)
+	test("BgBrightMagenta", au.Clear("x").BgBrightMagenta(), 0)
+	test("BgBrightCyan", au.Clear("x").BgBrightCyan(), 0)
+	test("BgBrightWhite", au.Clear("x").BgBrightWhite(), 0)
+	test("BgIndex", au.Clear("x").BgIndex(187), 0)
+	test("BgGray", au.Clear("x").BgGray(15), 0)
+	test("Colorize", au.Clear("x").Colorize(RedFg|BlueBg|BrightBg|BoldFm), 0)
 	// change
-	test("Reset", Bold("x").Reset(), 0)
-	test("Bold", Faint("x").Bold(), BoldFm)
-	test("Faint", Bold("x").Faint(), FaintFm)
-	test("DoublyUnderline", Reset("x").DoublyUnderline(), DoublyUnderlineFm)
-	test("Fraktur & Yellow", Reset("x").Yellow().Fraktur(), FrakturFm|YellowFg)
-	test("Italic", Reset("x").Italic(), ItalicFm)
-	test("Underline", Reset("x").Underline(), UnderlineFm)
-	test("SlowBlink", RapidBlink("x").SlowBlink(), SlowBlinkFm)
-	test("RapidBlink", SlowBlink("x").RapidBlink(), RapidBlinkFm)
-	test("Blink", Reset("x").Blink(), BlinkFm)
-	test("Reverse", Reset("x").Reverse(), ReverseFm)
-	test("Inverse", Reset("x").Inverse(), InverseFm)
-	test("Conceal", Reset("x").Conceal(), ConcealFm)
-	test("Hidden", Reset("x").Hidden(), HiddenFm)
-	test("CrossedOut", Reset("x").CrossedOut(), CrossedOutFm)
-	test("StrikeThrough", Reset("x").StrikeThrough(), StrikeThroughFm)
-	test("Framed", Reset("x").Framed(), FramedFm)
-	test("Encircled", Reset("x").Encircled(), EncircledFm)
-	test("Overlined", Reset("x").Overlined(), OverlinedFm)
-	test("Black", Reset("x").Black(), BlackFg)
-	test("Red", Reset("x").Red(), RedFg)
-	test("Green", Reset("x").Green(), GreenFg)
-	test("Yellow", Reset("x").Yellow(), YellowFg)
-	test("Brown", Reset("x").Brown(), BrownFg)
-	test("Blue", Reset("x").Blue(), BlueFg)
-	test("Magenta", Reset("x").Magenta(), MagentaFg)
-	test("Cyan", Reset("x").Cyan(), CyanFg)
-	test("White", Reset("x").White(), WhiteFg)
-	test("BrightBlack", Reset("x").BrightBlack(), BrightFg|BlackFg)
-	test("BrightRed", Reset("x").BrightRed(), BrightFg|RedFg)
-	test("BrightGreen", Reset("x").BrightGreen(), BrightFg|GreenFg)
-	test("BrightYellow", Reset("x").BrightYellow(), BrightFg|YellowFg)
-	test("BrightBlue", Reset("x").BrightBlue(), BrightFg|BlueFg)
-	test("BrightMagenta", Reset("x").BrightMagenta(), BrightFg|MagentaFg)
-	test("BrightCyan", Reset("x").BrightCyan(), BrightFg|CyanFg)
-	test("BrightWhite", Reset("x").BrightWhite(), BrightFg|WhiteFg)
-	test("Index", Reset("x").Index(178), (Color(178)<<shiftFg)|flagFg)
-	test("Gray", Reset("x").Gray(14), (Color(14+232)<<shiftFg)|flagFg)
-	test("BgBlack", Reset("x").BgBlack(), BlackBg)
-	test("BgRed", Reset("x").BgRed(), RedBg)
-	test("BgGreen", Reset("x").BgGreen(), GreenBg)
-	test("BgYellow", Reset("x").BgYellow(), YellowBg)
-	test("BgBrown", Reset("x").BgBrown(), BrownBg)
-	test("BgBlue", Reset("x").BgBlue(), BlueBg)
-	test("BgMagenta", Reset("x").BgMagenta(), MagentaBg)
-	test("BgCyan", Reset("x").BgCyan(), CyanBg)
-	test("BgWhite", Reset("x").BgWhite(), WhiteBg)
-	test("BgBrightBlack", Reset("x").BgBrightBlack(), BrightBg|BlackBg)
-	test("BgBrightRed", Reset("x").BgBrightRed(), BrightBg|RedBg)
-	test("BgBrightGreen", Reset("x").BgBrightGreen(), BrightBg|GreenBg)
-	test("BgBrightYellow", Reset("x").BgBrightYellow(), BrightBg|YellowBg)
-	test("BgBrightBlue", Reset("x").BgBrightBlue(), BrightBg|BlueBg)
-	test("BgBrightMagenta", Reset("x").BgBrightMagenta(), BrightBg|MagentaBg)
-	test("BgBrightCyan", Reset("x").BgBrightCyan(), BrightBg|CyanBg)
-	test("BgBrightWhite", Reset("x").BgBrightWhite(), BrightBg|WhiteBg)
-	test("BgIndex", Reset("x").BgIndex(187), Color(187)<<shiftBg|flagBg)
-	test("BgGray", Reset("x").BgGray(15), Color(232+15)<<shiftBg|flagBg)
-	test("Colorize", Reset("x").Colorize(RedFg|BlueBg|BrightBg|BoldFm),
+	au = New()
+	test("Reset", au.Bold("x").Reset(), 0)
+	test("Clear", au.Bold("x").Clear(), 0)
+	test("Bold", au.Faint("x").Bold(), BoldFm)
+	test("Faint", au.Bold("x").Faint(), FaintFm)
+	test("DoublyUnderline", au.Reset("x").DoublyUnderline(), DoublyUnderlineFm)
+	test("Fraktur & Yellow", au.Reset("x").Yellow().Fraktur(), FrakturFm|YellowFg)
+	test("Italic", au.Reset("x").Italic(), ItalicFm)
+	test("Underline", au.Reset("x").Underline(), UnderlineFm)
+	test("SlowBlink", au.RapidBlink("x").SlowBlink(), SlowBlinkFm)
+	test("RapidBlink", au.SlowBlink("x").RapidBlink(), RapidBlinkFm)
+	test("Blink", au.Reset("x").Blink(), BlinkFm)
+	test("Reverse", au.Reset("x").Reverse(), ReverseFm)
+	test("Inverse", au.Reset("x").Inverse(), InverseFm)
+	test("Conceal", au.Reset("x").Conceal(), ConcealFm)
+	test("Hidden", au.Reset("x").Hidden(), HiddenFm)
+	test("CrossedOut", au.Reset("x").CrossedOut(), CrossedOutFm)
+	test("StrikeThrough", au.Reset("x").StrikeThrough(), StrikeThroughFm)
+	test("Framed", au.Reset("x").Framed(), FramedFm)
+	test("Encircled", au.Reset("x").Encircled(), EncircledFm)
+	test("Overlined", au.Reset("x").Overlined(), OverlinedFm)
+	test("Black", au.Reset("x").Black(), BlackFg)
+	test("Red", au.Reset("x").Red(), RedFg)
+	test("Green", au.Reset("x").Green(), GreenFg)
+	test("Yellow", au.Reset("x").Yellow(), YellowFg)
+	test("Blue", au.Reset("x").Blue(), BlueFg)
+	test("Magenta", au.Reset("x").Magenta(), MagentaFg)
+	test("Cyan", au.Reset("x").Cyan(), CyanFg)
+	test("White", au.Reset("x").White(), WhiteFg)
+	test("BrightBlack", au.Reset("x").BrightBlack(), BrightFg|BlackFg)
+	test("BrightRed", au.Reset("x").BrightRed(), BrightFg|RedFg)
+	test("BrightGreen", au.Reset("x").BrightGreen(), BrightFg|GreenFg)
+	test("BrightYellow", au.Reset("x").BrightYellow(), BrightFg|YellowFg)
+	test("BrightBlue", au.Reset("x").BrightBlue(), BrightFg|BlueFg)
+	test("BrightMagenta", au.Reset("x").BrightMagenta(), BrightFg|MagentaFg)
+	test("BrightCyan", au.Reset("x").BrightCyan(), BrightFg|CyanFg)
+	test("BrightWhite", au.Reset("x").BrightWhite(), BrightFg|WhiteFg)
+	test("Index", au.Reset("x").Index(178), (Color(178)<<shiftFg)|flagFg)
+	test("Gray", au.Reset("x").Gray(14), (Color(14+232)<<shiftFg)|flagFg)
+	test("BgBlack", au.Reset("x").BgBlack(), BlackBg)
+	test("BgRed", au.Reset("x").BgRed(), RedBg)
+	test("BgGreen", au.Reset("x").BgGreen(), GreenBg)
+	test("BgYellow", au.Reset("x").BgYellow(), YellowBg)
+	test("BgBlue", au.Reset("x").BgBlue(), BlueBg)
+	test("BgMagenta", au.Reset("x").BgMagenta(), MagentaBg)
+	test("BgCyan", au.Reset("x").BgCyan(), CyanBg)
+	test("BgWhite", au.Reset("x").BgWhite(), WhiteBg)
+	test("BgBrightBlack", au.Reset("x").BgBrightBlack(), BrightBg|BlackBg)
+	test("BgBrightRed", au.Reset("x").BgBrightRed(), BrightBg|RedBg)
+	test("BgBrightGreen", au.Reset("x").BgBrightGreen(), BrightBg|GreenBg)
+	test("BgBrightYellow", au.Reset("x").BgBrightYellow(), BrightBg|YellowBg)
+	test("BgBrightBlue", au.Reset("x").BgBrightBlue(), BrightBg|BlueBg)
+	test("BgBrightMagenta", au.Reset("x").BgBrightMagenta(), BrightBg|MagentaBg)
+	test("BgBrightCyan", au.Reset("x").BgBrightCyan(), BrightBg|CyanBg)
+	test("BgBrightWhite", au.Reset("x").BgBrightWhite(), BrightBg|WhiteBg)
+	test("BgIndex", au.Reset("x").BgIndex(187), Color(187)<<shiftBg|flagBg)
+	test("BgGray", au.Reset("x").BgGray(15), Color(232+15)<<shiftBg|flagBg)
+	test("Colorize", au.Reset("x").Colorize(RedFg|BlueBg|BrightBg|BoldFm),
 		RedFg|BlueBg|BrightBg|BoldFm)
 	// overflow
-	test("Gray", Reset("x").Gray(151), Color(232+23)<<shiftFg|flagFg)
-	test("BgGray", Reset("x").BgGray(115), Color(232+23)<<shiftBg|flagBg)
+	test("Gray", au.Reset("x").Gray(151), Color(232+23)<<shiftFg|flagFg)
+	test("BgGray", au.Reset("x").BgGray(115), Color(232+23)<<shiftBg|flagBg)
+}
+
+func TestValue_hyperlinks(t *testing.T) {
+	const target = "http://example.com/path?query=value"
+	var (
+		au     = New()
+		params = []HyperlinkParam{
+			{
+				Key:   HyperlinkIDKey,
+				Value: "10",
+			},
+			{
+				Key:   "another",
+				Value: "custom",
+			},
+		}
+		val  Value
+		want string
+	)
+	val = au.Red("x").Hyperlink(target, HyperlinkID("10"),
+		HyperlinkParam{"another", "custom"})
+	assert.Equal(t, target, val.HyperlinkTarget())
+	assert.EqualValues(t, params, val.HyperlinkParams())
+	want = `]8;id=10:another=custom;http://example.com/path?` +
+		`query=value\x]8;;\`
+	assert.Equal(t, want, val.String())
+
+	// wrap a hyperlink
+	au = New(WithHyperlinks(false))
+	want = `http://example.com`
+	val = au.Red("x").Hyperlink("http://example.com")
+	assert.Equal(t, want, val.String())
+	assert.Equal(t, "http://example.com", val.HyperlinkTarget())
+	assert.Equal(t, "http://example.com", val.Value())
+
+	// no target, no parameters
+	val = au.Clear("x")
+	assert.Equal(t, "", val.HyperlinkTarget())
+	assert.Nil(t, val.HyperlinkParams())
 }
diff --git a/wrap.go b/wrap.go
index 44e1aa6..16f6786 100644
--- a/wrap.go
+++ b/wrap.go
@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2016-2020 The Aurora Authors. All rights reserved.
+// Copyright (c) 2016-2022 The Aurora Authors. All rights reserved.
 // This program is free software. It comes without any warranty,
 // to the extent permitted by applicable law. You can redistribute
 // it and/or modify it under the terms of the Unlicense. See LICENSE
@@ -35,34 +35,34 @@
 
 package aurora
 
-// Colorize wraps given value into Value with
-// given colors. For example
+// DefaultColorizer is global colorizer that used for package root color
+// methods.
+var DefaultColorizer = New(WithColors(true), WithHyperlinks(true))
+
+// Colorize wraps given value into Value with given colors. For example
 //
-//    s := Colorize("some", BlueFg|GreenBg|BoldFm)
+//	var s = Colorize("some", BlueFg|GreenBg|BoldFm)
 //
-// returns a Value with blue foreground, green
-// background and bold. Unlike functions like
-// Red/BgBlue/Bold etc. This function clears
-// all previous colors and formats. Thus
+// returns a Value with blue foreground, green background and bold. Unlike
+// functions like Red/BgBlue/Bold etc. This function clears all previous colors
+// and formats. Thus
 //
-//    s := Colorize(Red("some"), BgBlue)
+//	var s = Colorize(Red("some"), BgBlue)
 //
-// clears red color from value
+// clears red color from value.
 func Colorize(arg interface{}, color Color) Value {
-	if val, ok := arg.(value); ok {
-		val.color = color
-		return val
-	}
-	return value{arg, color, 0}
+	return DefaultColorizer.Colorize(arg, color)
 }
 
-// Reset wraps given argument returning Value
-// without formats and colors.
+// Reset wraps given argument returning Value without formats, colors and links.
 func Reset(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.Reset()
-	}
-	return value{value: arg}
+	return DefaultColorizer.Reset(arg)
+}
+
+// Clear wraps given argument returning Value without formats and colors. But
+// preserving links.
+func Clear(arg interface{}) Value {
+	return DefaultColorizer.Clear(arg)
 }
 
 //
@@ -71,142 +71,93 @@ func Reset(arg interface{}) Value {
 
 // Bold or increased intensity (1).
 func Bold(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.Bold()
-	}
-	return value{value: arg, color: BoldFm}
+	return DefaultColorizer.Bold(arg)
 }
 
-// Faint decreases intensity (2).
-// The Faint rejects the Bold.
+// Faint decreases intensity (2). The Faint rejects the Bold.
 func Faint(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.Faint()
-	}
-	return value{value: arg, color: FaintFm}
+	return DefaultColorizer.Faint(arg)
 }
 
-// DoublyUnderline or Bold off, double-underline
-// per ECMA-48 (21).
+// DoublyUnderline or Bold off, double-underline per ECMA-48 (21).
 func DoublyUnderline(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.DoublyUnderline()
-	}
-	return value{value: arg, color: DoublyUnderlineFm}
+	return DefaultColorizer.DoublyUnderline(arg)
 }
 
 // Fraktur is rarely supported (20).
 func Fraktur(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.Fraktur()
-	}
-	return value{value: arg, color: FrakturFm}
+	return DefaultColorizer.Fraktur(arg)
 }
 
-// Italic is not widely supported, sometimes
-// treated as inverse (3).
+// Italic is not widely supported, sometimes treated as inverse (3).
 func Italic(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.Italic()
-	}
-	return value{value: arg, color: ItalicFm}
+	return DefaultColorizer.Italic(arg)
 }
 
 // Underline (4).
 func Underline(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.Underline()
-	}
-	return value{value: arg, color: UnderlineFm}
+	return DefaultColorizer.Underline(arg)
 }
 
-// SlowBlink makes text blink less than
-// 150 per minute (5).
+// SlowBlink makes text blink less than 150 per minute (5).
 func SlowBlink(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.SlowBlink()
-	}
-	return value{value: arg, color: SlowBlinkFm}
+	return DefaultColorizer.SlowBlink(arg)
 }
 
-// RapidBlink makes text blink 150+ per
-// minute. It is not widely supported (6).
+// RapidBlink makes text blink 150+ per minute. It is not widely supported (6).
 func RapidBlink(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.RapidBlink()
-	}
-	return value{value: arg, color: RapidBlinkFm}
+	return DefaultColorizer.RapidBlink(arg)
 }
 
 // Blink is alias for the SlowBlink.
 func Blink(arg interface{}) Value {
-	return SlowBlink(arg)
+	return DefaultColorizer.Blink(arg)
 }
 
-// Reverse video, swap foreground and
-// background colors (7).
+// Reverse video, swap foreground and background colors (7).
 func Reverse(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.Reverse()
-	}
-	return value{value: arg, color: ReverseFm}
+	return DefaultColorizer.Reverse(arg)
 }
 
 // Inverse is alias for the Reverse
 func Inverse(arg interface{}) Value {
-	return Reverse(arg)
+	return DefaultColorizer.Inverse(arg)
 }
 
-// Conceal hides text, preserving an ability to select
-// the text and copy it. It is not widely supported (8).
+// Conceal hides text, preserving an ability to select the text and copy it. It
+// is not widely supported (8).
 func Conceal(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.Conceal()
-	}
-	return value{value: arg, color: ConcealFm}
+	return DefaultColorizer.Conceal(arg)
 }
 
 // Hidden is alias for the Conceal
 func Hidden(arg interface{}) Value {
-	return Conceal(arg)
+	return DefaultColorizer.Hidden(arg)
 }
 
-// CrossedOut makes characters legible, but
-// marked for deletion (9).
+// CrossedOut makes characters legible, but marked for deletion (9).
 func CrossedOut(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.CrossedOut()
-	}
-	return value{value: arg, color: CrossedOutFm}
+	return DefaultColorizer.CrossedOut(arg)
 }
 
 // StrikeThrough is alias for the CrossedOut.
 func StrikeThrough(arg interface{}) Value {
-	return CrossedOut(arg)
+	return DefaultColorizer.StrikeThrough(arg)
 }
 
 // Framed (51).
 func Framed(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.Framed()
-	}
-	return value{value: arg, color: FramedFm}
+	return DefaultColorizer.Framed(arg)
 }
 
 // Encircled (52).
 func Encircled(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.Encircled()
-	}
-	return value{value: arg, color: EncircledFm}
+	return DefaultColorizer.Encircled(arg)
 }
 
 // Overlined (53).
 func Overlined(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.Overlined()
-	}
-	return value{value: arg, color: OverlinedFm}
+	return DefaultColorizer.Overlined(arg)
 }
 
 //
@@ -216,73 +167,42 @@ func Overlined(arg interface{}) Value {
 
 // Black foreground color (30)
 func Black(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.Black()
-	}
-	return value{value: arg, color: BlackFg}
+	return DefaultColorizer.Black(arg)
 }
 
 // Red foreground color (31)
 func Red(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.Red()
-	}
-	return value{value: arg, color: RedFg}
+	return DefaultColorizer.Red(arg)
 }
 
 // Green foreground color (32)
 func Green(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.Green()
-	}
-	return value{value: arg, color: GreenFg}
+	return DefaultColorizer.Green(arg)
 }
 
 // Yellow foreground color (33)
 func Yellow(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.Yellow()
-	}
-	return value{value: arg, color: YellowFg}
-}
-
-// Brown foreground color (33)
-//
-// Deprecated: use Yellow instead, following specification
-func Brown(arg interface{}) Value {
-	return Yellow(arg)
+	return DefaultColorizer.Yellow(arg)
 }
 
 // Blue foreground color (34)
 func Blue(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.Blue()
-	}
-	return value{value: arg, color: BlueFg}
+	return DefaultColorizer.Blue(arg)
 }
 
 // Magenta foreground color (35)
 func Magenta(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.Magenta()
-	}
-	return value{value: arg, color: MagentaFg}
+	return DefaultColorizer.Magenta(arg)
 }
 
 // Cyan foreground color (36)
 func Cyan(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.Cyan()
-	}
-	return value{value: arg, color: CyanFg}
+	return DefaultColorizer.Cyan(arg)
 }
 
 // White foreground color (37)
 func White(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.White()
-	}
-	return value{value: arg, color: WhiteFg}
+	return DefaultColorizer.White(arg)
 }
 
 //
@@ -291,96 +211,61 @@ func White(arg interface{}) Value {
 
 // BrightBlack foreground color (90)
 func BrightBlack(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.BrightBlack()
-	}
-	return value{value: arg, color: BrightFg | BlackFg}
+	return DefaultColorizer.BrightBlack(arg)
 }
 
 // BrightRed foreground color (91)
 func BrightRed(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.BrightRed()
-	}
-	return value{value: arg, color: BrightFg | RedFg}
+	return DefaultColorizer.BrightRed(arg)
 }
 
 // BrightGreen foreground color (92)
 func BrightGreen(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.BrightGreen()
-	}
-	return value{value: arg, color: BrightFg | GreenFg}
+	return DefaultColorizer.BrightGreen(arg)
 }
 
 // BrightYellow foreground color (93)
 func BrightYellow(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.BrightYellow()
-	}
-	return value{value: arg, color: BrightFg | YellowFg}
+	return DefaultColorizer.BrightYellow(arg)
 }
 
 // BrightBlue foreground color (94)
 func BrightBlue(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.BrightBlue()
-	}
-	return value{value: arg, color: BrightFg | BlueFg}
+	return DefaultColorizer.BrightBlue(arg)
 }
 
 // BrightMagenta foreground color (95)
 func BrightMagenta(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.BrightMagenta()
-	}
-	return value{value: arg, color: BrightFg | MagentaFg}
+	return DefaultColorizer.BrightMagenta(arg)
 }
 
 // BrightCyan foreground color (96)
 func BrightCyan(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.BrightCyan()
-	}
-	return value{value: arg, color: BrightFg | CyanFg}
+	return DefaultColorizer.BrightCyan(arg)
 }
 
 // BrightWhite foreground color (97)
 func BrightWhite(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.BrightWhite()
-	}
-	return value{value: arg, color: BrightFg | WhiteFg}
+	return DefaultColorizer.BrightWhite(arg)
 }
 
 //
 // Other
 //
 
-// Index of pre-defined 8-bit foreground color
-// from 0 to 255 (38;5;n).
-//
-//       0-  7:  standard colors (as in ESC [ 30–37 m)
-//       8- 15:  high intensity colors (as in ESC [ 90–97 m)
-//      16-231:  6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
-//     232-255:  grayscale from black to white in 24 steps
+// Index of pre-defined 8-bit foreground color from 0 to 255 (38;5;n).
 //
-func Index(n uint8, arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.Index(n)
-	}
-	return value{value: arg, color: (Color(n) << shiftFg) | flagFg}
+//	  0-  7:  standard colors (as in ESC [ 30–37 m)
+//	  8- 15:  high intensity colors (as in ESC [ 90–97 m)
+//	 16-231:  6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
+//	232-255:  grayscale from black to white in 24 steps
+func Index(n ColorIndex, arg interface{}) Value {
+	return DefaultColorizer.Index(n, arg)
 }
 
 // Gray from 0 to 24.
-func Gray(n uint8, arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.Gray(n)
-	}
-	if n > 23 {
-		n = 23
-	}
-	return value{value: arg, color: (Color(232+n) << shiftFg) | flagFg}
+func Gray(n GrayIndex, arg interface{}) Value {
+	return DefaultColorizer.Gray(n, arg)
 }
 
 //
@@ -390,73 +275,42 @@ func Gray(n uint8, arg interface{}) Value {
 
 // BgBlack background color (40)
 func BgBlack(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.BgBlack()
-	}
-	return value{value: arg, color: BlackBg}
+	return DefaultColorizer.BgBlack(arg)
 }
 
 // BgRed background color (41)
 func BgRed(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.BgRed()
-	}
-	return value{value: arg, color: RedBg}
+	return DefaultColorizer.BgRed(arg)
 }
 
 // BgGreen background color (42)
 func BgGreen(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.BgGreen()
-	}
-	return value{value: arg, color: GreenBg}
+	return DefaultColorizer.BgGreen(arg)
 }
 
 // BgYellow background color (43)
 func BgYellow(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.BgYellow()
-	}
-	return value{value: arg, color: YellowBg}
-}
-
-// BgBrown background color (43)
-//
-// Deprecated: use BgYellow instead, following specification
-func BgBrown(arg interface{}) Value {
-	return BgYellow(arg)
+	return DefaultColorizer.BgYellow(arg)
 }
 
 // BgBlue background color (44)
 func BgBlue(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.BgBlue()
-	}
-	return value{value: arg, color: BlueBg}
+	return DefaultColorizer.BgBlue(arg)
 }
 
 // BgMagenta background color (45)
 func BgMagenta(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.BgMagenta()
-	}
-	return value{value: arg, color: MagentaBg}
+	return DefaultColorizer.BgMagenta(arg)
 }
 
 // BgCyan background color (46)
 func BgCyan(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.BgCyan()
-	}
-	return value{value: arg, color: CyanBg}
+	return DefaultColorizer.BgCyan(arg)
 }
 
 // BgWhite background color (47)
 func BgWhite(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.BgWhite()
-	}
-	return value{value: arg, color: WhiteBg}
+	return DefaultColorizer.BgWhite(arg)
 }
 
 //
@@ -465,94 +319,113 @@ func BgWhite(arg interface{}) Value {
 
 // BgBrightBlack background color (100)
 func BgBrightBlack(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.BgBrightBlack()
-	}
-	return value{value: arg, color: BrightBg | BlackBg}
+	return DefaultColorizer.BgBrightBlack(arg)
 }
 
 // BgBrightRed background color (101)
 func BgBrightRed(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.BgBrightRed()
-	}
-	return value{value: arg, color: BrightBg | RedBg}
+	return DefaultColorizer.BgBrightRed(arg)
 }
 
 // BgBrightGreen background color (102)
 func BgBrightGreen(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.BgBrightGreen()
-	}
-	return value{value: arg, color: BrightBg | GreenBg}
+	return DefaultColorizer.BgBrightGreen(arg)
 }
 
 // BgBrightYellow background color (103)
 func BgBrightYellow(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.BgBrightYellow()
-	}
-	return value{value: arg, color: BrightBg | YellowBg}
+	return DefaultColorizer.BgBrightYellow(arg)
 }
 
 // BgBrightBlue background color (104)
 func BgBrightBlue(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.BgBrightBlue()
-	}
-	return value{value: arg, color: BrightBg | BlueBg}
+	return DefaultColorizer.BgBrightBlue(arg)
 }
 
 // BgBrightMagenta background color (105)
 func BgBrightMagenta(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.BgBrightMagenta()
-	}
-	return value{value: arg, color: BrightBg | MagentaBg}
+	return DefaultColorizer.BgBrightMagenta(arg)
 }
 
 // BgBrightCyan background color (106)
 func BgBrightCyan(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.BgBrightCyan()
-	}
-	return value{value: arg, color: BrightBg | CyanBg}
+	return DefaultColorizer.BgBrightCyan(arg)
 }
 
 // BgBrightWhite background color (107)
 func BgBrightWhite(arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.BgBrightWhite()
-	}
-	return value{value: arg, color: BrightBg | WhiteBg}
+	return DefaultColorizer.BgBrightWhite(arg)
 }
 
 //
 // Other
 //
 
-// BgIndex of 8-bit pre-defined background color
-// from 0 to 255 (48;5;n).
-//
-//       0-  7:  standard colors (as in ESC [ 40–47 m)
-//       8- 15:  high intensity colors (as in ESC [100–107 m)
-//      16-231:  6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
-//     232-255:  grayscale from black to white in 24 steps
+// BgIndex of 8-bit pre-defined background color from 0 to 255 (48;5;n).
 //
-func BgIndex(n uint8, arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.BgIndex(n)
-	}
-	return value{value: arg, color: (Color(n) << shiftBg) | flagBg}
+//	  0-  7:  standard colors (as in ESC [ 40–47 m)
+//	  8- 15:  high intensity colors (as in ESC [100–107 m)
+//	 16-231:  6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
+//	232-255:  grayscale from black to white in 24 steps
+func BgIndex(n ColorIndex, arg interface{}) Value {
+	return DefaultColorizer.BgIndex(n, arg)
 }
 
 // BgGray from 0 to 24.
-func BgGray(n uint8, arg interface{}) Value {
-	if val, ok := arg.(Value); ok {
-		return val.BgGray(n)
-	}
-	if n > 23 {
-		n = 23
-	}
-	return value{value: arg, color: (Color(n+232) << shiftBg) | flagBg}
+func BgGray(n GrayIndex, arg interface{}) Value {
+	return DefaultColorizer.BgGray(n, arg)
+}
+
+//
+// Hyperlinks feature
+//
+
+// Hyperlink with given target and parameters. If hyperlinks feature is
+// disabled, then the 'arg' argument dropped and the 'target' used instead
+// inheriting all colors and format from the 'arg' (if it's a Colored).
+//
+// See https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
+// for details about the hyperlinks feature.
+//
+// The Hyperlink doesn't escape the target and the params. They should be
+// checked and escaped before.
+//
+// See also HyperlinkID function.
+//
+// For a simple example
+//
+//	au.Hyperlink("Example", "http://example.com")
+//
+// and an example with ID
+//
+//	au.Hyperlink("Example", "http://example.com", aurora.HyperlinkID("10"))
+func Hyperlink(arg interface{}, target string, params ...HyperlinkParam) Value {
+	return DefaultColorizer.Hyperlink(arg, target, params...)
+}
+
+// HyperlinkTarget of the argument if it's a Value.
+func HyperlinkTarget(arg interface{}) (target string) {
+	return DefaultColorizer.HyperlinkTarget(arg)
+}
+
+// HyperlinkParams of the argument if it's a Value.
+func HyperlinkParams(arg interface{}) (params []HyperlinkParam) {
+	return DefaultColorizer.HyperlinkParams(arg)
+}
+
+// Sprintf allows to use Value as format. For example
+//
+//	var v = Sprintf(Red("total: +3.5f points"), Blue(3.14))
+//
+// In this case "total:" and "points" will be red, but
+// 3.14 will be blue. But, in another example
+//
+//	var v = Sprintf(Red("total: +3.5f points"), 3.14)
+//
+// full string will be red. And no way to clear 3.14 to default format and
+// color.
+//
+// It applies own configurations to all given Values.
+func Sprintf(format interface{}, args ...interface{}) string {
+	return DefaultColorizer.Sprintf(format, args...)
 }
diff --git a/wrap_test.go b/wrap_test.go
index ddd31b3..e2e644a 100644
--- a/wrap_test.go
+++ b/wrap_test.go
@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2016-2020 The Aurora Authors. All rights reserved.
+// Copyright (c) 2016-2022 The Aurora Authors. All rights reserved.
 // This program is free software. It comes without any warranty,
 // to the extent permitted by applicable law. You can redistribute
 // it and/or modify it under the terms of the Unlicense. See LICENSE
@@ -37,6 +37,8 @@ package aurora
 
 import (
 	"testing"
+
+	"github.com/stretchr/testify/assert"
 )
 
 func testFunc(t *testing.T, name string, v Value, clr Color) {
@@ -46,9 +48,6 @@ func testFunc(t *testing.T, name string, v Value, clr Color) {
 	} else if str != "x" {
 		t.Errorf("%s wrong value: '%v', expected 'x'", name, v.Value())
 	}
-	if !isTail(v, 0) {
-		t.Errorf("%s wrong tail: %d", name, v.tail())
-	}
 	if !isColor(v, clr) {
 		t.Errorf("%s wrong color: %d, want: %d", name, v.Color(), clr)
 	}
@@ -60,6 +59,12 @@ func Test_Reset(t *testing.T) {
 		0)
 }
 
+func Test_Clear(t *testing.T) {
+	testFunc(t, "Clear", Clear("x"), 0)
+	testFunc(t, "Complex Clear", Clear(DoublyUnderline(Underline("x"))),
+		0)
+}
+
 func Test_Bold(t *testing.T) {
 	testFunc(t, "Bold", Bold("x"), BoldFm)
 	testFunc(t, "Complex Bold", Bold(Italic(Red("x"))),
@@ -192,12 +197,6 @@ func Test_Yellow(t *testing.T) {
 		YellowFg|BoldFm|BrightBg|MagentaBg)
 }
 
-func Test_Brown(t *testing.T) {
-	testFunc(t, "Brown", Brown("x"), BrownFg)
-	testFunc(t, "Complex Brown", Brown(BgBrightBlue("x").Bold()),
-		BrownFg|BrightBg|BlueBg|BoldFm)
-}
-
 func Test_Blue(t *testing.T) {
 	testFunc(t, "Blue", Blue("x"), BlueFg)
 	testFunc(t, "Complex Blue", Blue(Fraktur("x").Underline()),
@@ -306,12 +305,6 @@ func Test_BgYellow(t *testing.T) {
 		YellowBg|BlinkFm)
 }
 
-func Test_BgBrown(t *testing.T) {
-	testFunc(t, "BgBrown", BgBrown("x"), BrownBg)
-	testFunc(t, "Complex BgBrown", BgBrown(Hidden("x")),
-		BrownBg|HiddenFm)
-}
-
 func Test_BgBlue(t *testing.T) {
 	testFunc(t, "BgBlue", BgBlue("x"), BlueBg)
 	testFunc(t, "Complex BgBlue", BgBlue(Framed("x")),
@@ -408,3 +401,53 @@ func Test_bigGray(t *testing.T) {
 	testFunc(t, "Gray", Gray(115, "x"), Color(232+23)<<shiftFg|flagFg)
 	testFunc(t, "BgGray", BgGray(215, "x"), Color(232+23)<<shiftBg|flagBg)
 }
+
+func Test_Hyperlink(t *testing.T) {
+	assert.EqualValues(t,
+		Value{
+			value: "Example",
+			cc:    DefaultColorizer.cc,
+			hyperlink: &hyperlink{
+				target: "http://example.com",
+				params: []HyperlinkParam{{
+					Key:   "id",
+					Value: "10",
+				}},
+			},
+		},
+		Hyperlink("Example", "http://example.com", HyperlinkID("10")))
+
+	assert.EqualValues(t,
+		Value{
+			value: "Example",
+			cc:    DefaultColorizer.cc | colorConfig(RedFg),
+			hyperlink: &hyperlink{
+				target: "http://example.com",
+				params: []HyperlinkParam{{
+					Key:   "id",
+					Value: "10",
+				}},
+			},
+		},
+		Hyperlink(Red("Example"), "http://example.com", HyperlinkID("10")))
+}
+
+func Test_HyperlinkTarget(t *testing.T) {
+	assert.Equal(t, "", HyperlinkTarget("Example"))
+	assert.Equal(t, "http://example.com",
+		HyperlinkTarget(Hyperlink(
+			Red("Example"), "http://example.com", HyperlinkID("10")),
+		))
+}
+
+func Test_HyperlinkParams(t *testing.T) {
+	assert.Equal(t, HyperlinkParams(nil), HyperlinkParams("Example"))
+	assert.EqualValues(t,
+		[]HyperlinkParam{{
+			Key:   "id",
+			Value: "10",
+		}},
+		HyperlinkParams(Hyperlink(
+			Red("Example"), "http://example.com", HyperlinkID("10")),
+		))
+}

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/share/gocode/src/github.com/logrusorgru/aurora/config.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/logrusorgru/aurora/config_test.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/logrusorgru/aurora/go.sum
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/logrusorgru/aurora/hyperlinks.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/logrusorgru/aurora/hyperlinks_test.go

No differences were encountered in the control files

More details

Full run details