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

Motivation
Unrestricted OverloadedLabels syntax 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. 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:
-- 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 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.
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.