packages feed

webcolor-labels-0.1.0.0: README.md

# webcolor-labels

Zero-dependency, plug-and-play library that enables #hex-color syntax for your own types!

![Demo image](https://raw.githubusercontent.com/haskell-game/webcolor-labels/refs/heads/master/images/demo.png)

## Motivation

[Unrestricted OverloadedLabels syntax](https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0170-unrestricted-overloadedlabels.rst) was implemented in GHC 9.6.1. It isn't hard to notice
that this syntax is a strict superset of hexadecimal CSS color syntax, a.k.a. [Web colors](https://en.wikipedia.org/wiki/Web_colors). It would be great if GUI libraries could take advantage of this
fact and allow their users to write `#f00` for the color "red," or even `#red`, right?

That's where `webcolor-labels` comes into play. This library implements type-level string parsing and validation
and provides you an easy-to-use interface for defining an `IsLabel` instance. In fact, it's as easy as counting
one, two, three:

```haskell
-- one
import WebColor.Labels
import GHC.OverloadedLabels

-- two
instance IsWebColorAlpha s => IsLabel s YourColor where
  fromLabel = webColorAlpha @s yourColorFromWord8

-- three
yourColorFromWord8 :: Word8 -> Word8 -> Word8 -> Word8 -> YourColor
yourColorFromWord8 red green blue alpha = ...
```

And that's all!

## Syntax

Allowed colors aim to follow the [Wikipedia Web Colors](https://en.wikipedia.org/wiki/Web_colors) page; here is a quick recap:

A color is written as a hex triplet, which is a six-digit (e.g., #fa12c7) or eight-digit (e.g., #fa12c7aa) hexadecimal number. The bytes represent the red, green, blue, and optional alpha channels of the color; hence we have #rrggbbaa.

It is possible to use the shorthand form with three and four digits: #f8c = #ff88cc and #f8c3 = #ff88cc33.

The syntax also supports 16 basic colors for convenience:

| Color name | Hex value |
| :--------- | :-------: |
| #white     | #FFFFFF |
| #silver    | #C0C0C0 |
| #gray      | #808080 |
| #black     | #000000 |
| #red       | #FF0000 |
| #maroon    | #800000 |
| #yellow    | #FFFF00 |
| #olive     | #808000 |
| #lime      | #00FF00 |
| #green     | #008000 |
| #aqua      | #00FFFF |
| #teal      | #008080 |
| #blue      | #0000FF |
| #navy      | #000080 |
| #fuchsia   | #FF00FF |
| #purple    | #800080 |

Hex triplet form is case-insensitive; therefore, #fff is the same as #FFF, but basic colors are case-sensitive.
That means #red is the same as #f00, but #RED and #Red result in a compile-time error.

## FAQ

### I want to use this syntax with _$LIBRARYNAME_, what should I do?

`webcolor-label`'s primary users are other library authors; therefore, you should go to the _$LIBRARYNAME_'s issue tracker and tell them that `webcolor-labels` will improve the lives of their users.

Alternatively, you may write an orphan instance, but it's a bad idea in general and you should avoid that as much as possible.

### `generic-lens` uses the same syntax. Does that mean it will conflict with an instance defined using `webcolor-labels`?

No, unless you define a highly polymorphic `IsLabel` instance or your color is a type alias for a function.

The `generic-lens` instance applies only if a function is expected in place of `#label`. Therefore, define instances with a concrete head, and everything will work smoothly.

### But what about instances for the color types from different libraries? Might they conflict?

No, if each instance is defined correctly.

### I have a type class to represent colors. How can I use `#` syntax with a function that accepts my type class?

Unfortunately, my library just doesn't fit this use case. It's the same problem as `show . read`; GHC just can't infer a type in the middle.

### I want to use different/custom values for named colors, but `webcolor-labels` provides pre-installed values. What should I do in that case?

You can still use type-level parsing from `webcolor-labels` and build custom routing on top of that, but you have to lower that into values yourself.

## Contact info and acknowledgements

If you have any questions, you can email me using `me@sandwitch.dev`. Alternatively, you can DM me on Matrix (`@root:sandwitch.dev`) or Telegram (`@sand_witch`).

Many thanks to the Russian Haskell gamedev community, who encouraged me to convert this code into a real library and assisted with shaping the API.