├── CODEOWNERS
├── test
├── gleam_community_colour_test.gleam
├── colour
│ └── accessibility.gleam
└── colour.gleam
├── .gitignore
├── CHANGELOG.md
├── gleam.toml
├── .github
└── workflows
│ ├── test.yml
│ └── release.yml
├── manifest.toml
├── README.md
├── CODE_OF_CONDUCT.md
├── src
└── gleam_community
│ ├── colour
│ └── accessibility.gleam
│ └── colour.gleam
└── LICENCE
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @brettkolodny
2 |
--------------------------------------------------------------------------------
/test/gleam_community_colour_test.gleam:
--------------------------------------------------------------------------------
1 | import gleeunit
2 |
3 | pub fn main() {
4 | gleeunit.main()
5 | }
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .env*
3 | .idea/
4 | .nova/
5 | .vscode/
6 | build/
7 | coverage/
8 | dist/
9 | erl_crash.dump
10 | node_modules/
11 | *.beam
12 | *.ez
13 | *.log
14 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 1.1.0 - Unreleased
4 |
5 | - Updated dependencies and code to be in line with Gleam 0.27.0.
6 |
7 | ## 1.0.0
8 |
9 | - Initial Release
10 |
--------------------------------------------------------------------------------
/gleam.toml:
--------------------------------------------------------------------------------
1 | name = "gleam_community_colour"
2 | version = "2.0.2"
3 | licences = ["Apache-2.0"]
4 | description = "Colour types, conversions, and other utilities"
5 | repository = { type = "github", user = "gleam-community", repo = "colour" }
6 | gleam = ">= 1.4.0"
7 |
8 | [dependencies]
9 | gleam_stdlib = ">= 0.50.0 and < 2.0.0"
10 | gleam_json = ">= 2.2.0 and < 4.0.0"
11 |
12 | [dev-dependencies]
13 | gleeunit = ">= 1.3.0 and < 2.0.0"
14 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: main
2 |
3 | on:
4 | push:
5 | branches: ["main"]
6 | pull_request:
7 | branches: ["**"]
8 |
9 | concurrency:
10 | group: ${{ github.workflow }}-${{ github.ref }}
11 | cancel-in-progress: true
12 |
13 | jobs:
14 | build:
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - uses: actions/checkout@v3.1.0
19 | - uses: erlef/setup-beam@v1.18.2
20 | with:
21 | otp-version: "27.2.1"
22 | gleam-version: "1.11.1"
23 | rebar3-version: "3"
24 |
25 | - run: gleam format --check
26 |
27 | - run: gleam test --target erlang
28 |
29 | - run: gleam test --target javascript
30 |
--------------------------------------------------------------------------------
/test/colour/accessibility.gleam:
--------------------------------------------------------------------------------
1 | import gleam_community/colour
2 | import gleam_community/colour/accessibility
3 | import gleeunit/should
4 |
5 | pub fn contrast_ratio_test() {
6 | accessibility.contrast_ratio(colour.white, colour.black)
7 | |> should.equal(21.0)
8 |
9 | accessibility.contrast_ratio(colour.pink, colour.pink)
10 | |> should.equal(1.0)
11 |
12 | accessibility.contrast_ratio(colour.pink, colour.black)
13 | |> should.equal(accessibility.contrast_ratio(colour.black, colour.pink))
14 | }
15 |
16 | pub fn luminance_test() {
17 | accessibility.luminance(colour.black)
18 | |> should.equal(0.0)
19 |
20 | accessibility.luminance(colour.white)
21 | |> should.equal(1.0)
22 | }
23 |
24 | pub fn maximum_contrast_test() {
25 | accessibility.maximum_contrast(colour.yellow, [
26 | colour.white,
27 | colour.dark_blue,
28 | colour.green,
29 | ])
30 | |> should.equal(Ok(colour.dark_blue))
31 | }
32 |
--------------------------------------------------------------------------------
/manifest.toml:
--------------------------------------------------------------------------------
1 | # This file was generated by Gleam
2 | # You typically do not need to edit this file
3 |
4 | packages = [
5 | { name = "gleam_json", version = "3.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "874FA3C3BB6E22DD2BB111966BD40B3759E9094E05257899A7C08F5DE77EC049" },
6 | { name = "gleam_stdlib", version = "0.61.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "3DC407D6EDA98FCE089150C11F3AD892B6F4C3CA77C87A97BAE8D5AB5E41F331" },
7 | { name = "gleeunit", version = "1.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "63022D81C12C17B7F1A60E029964E830A4CBD846BBC6740004FC1F1031AE0326" },
8 | ]
9 |
10 | [requirements]
11 | gleam_json = { version = ">= 2.2.0 and < 4.0.0" }
12 | gleam_stdlib = { version = ">= 0.50.0 and < 2.0.0" }
13 | gleeunit = { version = ">= 1.3.0 and < 2.0.0" }
14 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: release
2 |
3 | on:
4 | push:
5 | tags: ["v*"]
6 |
7 | jobs:
8 | publish:
9 | runs-on: ubuntu-latest
10 |
11 | steps:
12 | - uses: actions/checkout@v3.1.0
13 | - uses: erlef/setup-beam@v1.18.2
14 | with:
15 | otp-version: "27.2.1"
16 | gleam-version: "1.11.1"
17 | rebar3-version: "3"
18 |
19 | - run: |
20 | version="v$(cat gleam.toml | grep -m 1 "version" | sed -r "s/version *= *\"([[:digit:].]+)\"/\1/")"
21 | if [ "$version" != "${{ github.ref_name }}" ]; then
22 | echo "tag '${{ github.ref_name }}' does not match the version in gleam.toml"
23 | echo "expected a tag name 'v$version'"
24 | exit 1
25 | fi
26 | name: check version
27 |
28 | - run: gleam format --check
29 |
30 | - run: gleam test --target erlang
31 |
32 | - run: gleam test --target javascript
33 |
34 | - run: gleam publish -y
35 | env:
36 | HEXPM_USER: ${{ secrets.HEX_USERNAME }}
37 | HEXPM_PASS: ${{ secrets.HEX_PASSWORD }}
38 |
39 | - uses: softprops/action-gh-release@v1
40 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # gleam-community/colour
2 |
3 | A package for a standard Colour type, conversions, and other utilities.
4 |
5 | [](https://hex.pm/packages/gleam_community_colour)
6 | [](https://hexdocs.pm/gleam_community_colour/)
7 |
8 | ✨ This project is written in pure Gleam so you can use it anywhere Gleam runs: Erlang, Elixir, Node, Deno, and the browser!
9 |
10 | ---
11 |
12 | ## Quickstart
13 |
14 | ```gleam
15 | import gleam_community/colour
16 | import gleam_community/colour/accessibility
17 |
18 | pub fn main() {
19 | let foreground = colour.from_hsl(h: 0.858, s: 1.0, l: 0.843)
20 |
21 | let background_options = [colour.light_grey, colour.dark_grey]
22 |
23 | let background = accessibility.maximum_contrast(foreground, background_options)
24 | }
25 | ```
26 |
27 | ## Installation
28 |
29 | `gleam_community` packages are published to [hex.pm](https://hex.pm/packages/gleam_community_colour)
30 | with the prefix `gleam_community_`. You can add them to your Gleam projects directly:
31 |
32 | ```sh
33 | gleam add gleam_community_colour
34 | ```
35 |
36 | The docs can be found over at [hexdocs.pm](https://hexdocs.pm/gleam_community_colour).
37 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | - Using welcoming and inclusive language
18 | - Being respectful of differing viewpoints and experiences
19 | - Gracefully accepting constructive criticism
20 | - Focusing on what is best for the community
21 | - Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | - The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | - Trolling, insulting/derogatory comments, and personal or political attacks
28 | - Public or private harassment
29 | - Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | - Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting one of the organizers or moderators of the gleam-community
59 | organization. All complaints will be reviewed and investigated and will result in a
60 | response that is deemed necessary and appropriate to the circumstances. The project team
61 | is obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is a direct decendant of the [Gleam Code of Conduct][gleam coc],
71 | which is itself a decendant of the [Contributor Covenant (v1.4)][cc].
72 |
73 | [gleam coc]: https://github.com/gleam-lang/gleam/blob/f793b5d28a3102276a8b861c7e16a19c5231426e/CODE_OF_CONDUCT.md
74 | [cc]: https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
75 |
--------------------------------------------------------------------------------
/src/gleam_community/colour/accessibility.gleam:
--------------------------------------------------------------------------------
1 | ////
2 | //// - **Accessibility**
3 | //// - [`luminance`](#luminance)
4 | //// - [`contrast_ratio`](#contrast_ratio)
5 | //// - [`maximum_contrast`](#maximum_contrast)
6 | ////
7 | //// ---
8 | ////
9 | //// This package was heavily inspired by the `elm-color-extra` module.
10 | //// The original source code can be found
11 | //// here.
12 | ////
13 | ////
14 | //// The license of that package is produced below:
15 | ////
16 | ////
17 | //// > MIT License
18 | ////
19 | //// > Copyright (c) 2016 Andreas Köberle
20 | ////
21 | //// > Permission is hereby granted, free of charge, to any person obtaining a copy
22 | //// of this software and associated documentation files (the "Software"), to deal
23 | //// in the Software without restriction, including without limitation the rights
24 | //// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
25 | //// copies of the Software, and to permit persons to whom the Software is
26 | //// furnished to do so, subject to the following conditions:
27 | ////
28 | //// > The above copyright notice and this permission notice shall be included in all
29 | //// copies or substantial portions of the Software.
30 | ////
31 | //// > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32 | //// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33 | //// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
34 | //// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
35 | //// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
36 | //// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
37 | //// SOFTWARE.
38 | ////
39 | ////
40 | ////
41 |
42 | // Just in case we decide in the future to no longer include the above reference
43 | // and license, this package was initially a port of the `elm-color-extra` module:
44 | //
45 | // https://github.com/noahzgordon/elm-color-extra
46 | //
47 |
48 | // IMPORTS --------------------------------------------------------------------
49 |
50 | import gleam/float
51 | import gleam/list
52 | import gleam_community/colour.{type Colour}
53 |
54 | // UTILITIES ------------------------------------------------------------------
55 |
56 | fn intensity(colour_value: Float) -> Float {
57 | // Calculation taken from https://www.w3.org/TR/WCAG20/#relativeluminancedef
58 | case True {
59 | _ if colour_value <=. 0.03928 -> colour_value /. 12.92
60 | _ -> {
61 | // Is this guaranteed to be `OK`?
62 | let assert Ok(i) = float.power({ colour_value +. 0.055 } /. 1.055, 2.4)
63 | i
64 | }
65 | }
66 | }
67 |
68 | // ACCESSIBILITY --------------------------------------------------------------
69 |
70 | /// Returns the relative brightness of the given `Colour` as a `Float` between
71 | /// 0.0, and 1.0 with 0.0 being the darkest possible colour and 1.0 being the lightest.
72 | ///
73 | ///
74 | /// Example:
75 | ///
76 | /// ```gleam
77 | /// fn example() {
78 | /// luminance(colour.white) // 1.0
79 | /// }
80 | /// ```
81 | ///
82 | ///
83 | ///
91 | ///
92 | pub fn luminance(colour: Colour) -> Float {
93 | // Calculation taken from https://www.w3.org/TR/WCAG20/#relativeluminancedef
94 | let #(r, g, b, _) = colour.to_rgba(colour)
95 |
96 | let r_intensity = intensity(r)
97 | let g_intensity = intensity(g)
98 | let b_intensity = intensity(b)
99 |
100 | 0.2126 *. r_intensity +. 0.7152 *. g_intensity +. 0.0722 *. b_intensity
101 | }
102 |
103 | /// Returns the contrast between two `Colour` values as a `Float` between 1.0,
104 | /// and 21.0 with 1.0 being no contrast and, 21.0 being the highest possible contrast.
105 | ///
106 | ///
107 | /// Example:
108 | ///
109 | /// ```gleam
110 | /// fn example() {
111 | /// contrast_ratio(between: colour.white, and: colour.black) // 21.0
112 | /// }
113 | /// ```
114 | ///
115 | ///
116 | ///
124 | ///
125 | pub fn contrast_ratio(between colour_a: Colour, and colour_b: Colour) -> Float {
126 | // Calculation taken from https://www.w3.org/TR/WCAG20/#contrast-ratiodef
127 | let luminance_a = luminance(colour_a) +. 0.05
128 | let luminance_b = luminance(colour_b) +. 0.05
129 |
130 | case luminance_a >. luminance_b {
131 | True -> luminance_a /. luminance_b
132 | False -> luminance_b /. luminance_a
133 | }
134 | }
135 |
136 | /// Returns the `Colour` with the highest contrast between the base `Colour`,
137 | /// and and the other provided `Colour` values.
138 | ///
139 | ///
140 | /// Example:
141 | ///
142 | /// ```gleam
143 | /// fn example() {
144 | /// maximum_contrast(
145 | /// colour.yellow,
146 | /// [colour.white, colour.dark_blue, colour.green],
147 | /// )
148 | /// }
149 | /// ```
150 | ///
151 | ///
152 | ///
160 | ///
161 | pub fn maximum_contrast(
162 | base: Colour,
163 | colours: List(Colour),
164 | ) -> Result(Colour, Nil) {
165 | colours
166 | |> list.sort(fn(colour_a, colour_b) {
167 | let contrast_a = contrast_ratio(base, colour_a)
168 | let contrast_b = contrast_ratio(base, colour_b)
169 |
170 | float.compare(contrast_b, contrast_a)
171 | })
172 | |> list.first()
173 | }
174 |
--------------------------------------------------------------------------------
/test/colour.gleam:
--------------------------------------------------------------------------------
1 | import gleam/json
2 | import gleam_community/colour
3 | import gleeunit/should
4 |
5 | pub fn from_rgb255_test() {
6 | colour.from_rgb255(204, 0, 0)
7 | |> should.equal(Ok(colour.red))
8 |
9 | colour.from_rgb255(114, 159, 207)
10 | |> should.equal(Ok(colour.light_blue))
11 |
12 | colour.from_rgb255(255, 175, 243)
13 | |> should.equal(Ok(colour.pink))
14 | }
15 |
16 | pub fn negative_from_rgb255_test() {
17 | colour.from_rgb255(-1, 0, 0)
18 | |> should.equal(Error(Nil))
19 | }
20 |
21 | pub fn too_large_from_from_rgb255_test() {
22 | colour.from_rgb255(256, 0, 0)
23 | |> should.equal(Error(Nil))
24 | }
25 |
26 | pub fn from_rgb_test() {
27 | colour.from_rgb(1.0, 0.6862745098039216, 0.9529411764705882)
28 | |> should.equal(Ok(colour.pink))
29 | }
30 |
31 | pub fn negative_from_rgb_test() {
32 | colour.from_rgb(-1.0, 0.0, 0.0)
33 | |> should.equal(Error(Nil))
34 | }
35 |
36 | pub fn too_large_from_rgb_test() {
37 | colour.from_rgb(256.0, 0.0, 0.0)
38 | |> should.equal(Error(Nil))
39 | }
40 |
41 | pub fn from_rgba_test() {
42 | colour.from_rgba(1.0, 0.6862745098039216, 0.9529411764705882, 1.0)
43 | |> should.equal(Ok(colour.pink))
44 |
45 | let assert Ok(pink_half_opacity) =
46 | colour.from_rgba(1.0, 0.6862745098039216, 0.9529411764705882, 0.5)
47 |
48 | pink_half_opacity
49 | |> colour.to_rgba()
50 | |> should.equal(#(1.0, 0.6862745098039216, 0.9529411764705882, 0.5))
51 | }
52 |
53 | pub fn negative_from_rgba_test() {
54 | colour.from_rgba(-1.0, 0.0, 0.0, 1.0)
55 | |> should.equal(Error(Nil))
56 |
57 | colour.from_rgba(1.0, 0.0, 0.0, -1.0)
58 | |> should.equal(Error(Nil))
59 | }
60 |
61 | pub fn too_large_rgba_test() {
62 | colour.from_rgba(1.1, 0.0, 0.0, 1.0)
63 | |> should.equal(Error(Nil))
64 |
65 | colour.from_rgba(256.0, 0.0, 0.0, 2.0)
66 | |> should.equal(Error(Nil))
67 | }
68 |
69 | pub fn from_rgb_hex_test() {
70 | colour.from_rgb_hex(0xffaff3)
71 | |> should.equal(Ok(colour.pink))
72 | }
73 |
74 | pub fn negative_from_rgb_hex_test() {
75 | colour.from_rgb_hex(-1 * 0xffaff3)
76 | |> should.equal(Error(Nil))
77 | }
78 |
79 | pub fn too_large_from_rgb_hex_test() {
80 | colour.from_rgb_hex(0xfffaff3)
81 | |> should.equal(Error(Nil))
82 | }
83 |
84 | pub fn from_rgb_hex_string_test() {
85 | colour.from_rgb_hex_string("#ffaff3")
86 | |> should.equal(Ok(colour.pink))
87 |
88 | colour.from_rgb_hex_string("0xffaff3")
89 | |> should.equal(Ok(colour.pink))
90 |
91 | colour.from_rgb_hex_string("ffaff3")
92 | |> should.equal(Ok(colour.pink))
93 | }
94 |
95 | pub fn too_large_from_rgb_hex_string_test() {
96 | colour.from_rgb_hex_string("#fffaff3")
97 | |> should.equal(Error(Nil))
98 | }
99 |
100 | pub fn from_hsl_test() {
101 | let assert Ok(c) = colour.from_hsl(0.25, 0.25, 0.5)
102 |
103 | c
104 | |> colour.to_rgba()
105 | |> should.equal(#(0.5, 0.625, 0.375, 1.0))
106 | }
107 |
108 | pub fn negative_from_hsl_test() {
109 | colour.from_hsl(-0.25, 0.25, 0.5)
110 | |> should.equal(Error(Nil))
111 | }
112 |
113 | pub fn too_large_from_hsl_test() {
114 | colour.from_hsl(25.0, 0.25, 0.5)
115 | |> should.equal(Error(Nil))
116 | }
117 |
118 | pub fn from_hsla_test() {
119 | let assert Ok(c) = colour.from_hsla(h: 0.25, s: 0.25, l: 0.5, a: 1.0)
120 |
121 | colour.to_rgba(c)
122 | |> should.equal(#(0.5, 0.625, 0.375, 1.0))
123 | }
124 |
125 | pub fn negative_from_hsla_test() {
126 | colour.from_hsla(h: -0.25, s: 0.25, l: 0.25, a: 1.0)
127 | |> should.equal(Error(Nil))
128 | }
129 |
130 | pub fn too_large_from_hsla_test() {
131 | colour.from_hsla(h: 25.0, s: 0.25, l: 0.25, a: 1.0)
132 | |> should.equal(Error(Nil))
133 | }
134 |
135 | pub fn to_rgba_hex_string_test() {
136 | let assert Ok(c) = colour.from_rgba(r: 1.0, g: 1.0, b: 1.0, a: 1.0)
137 |
138 | c
139 | |> colour.to_rgba_hex_string()
140 | |> should.equal("FFFFFFFF")
141 |
142 | let assert Ok(c) = colour.from_rgba(r: 1.0, g: 0.0, b: 0.0, a: 1.0)
143 |
144 | c
145 | |> colour.to_rgba_hex_string()
146 | |> should.equal("FF0000FF")
147 |
148 | colour.pink
149 | |> colour.to_rgba_hex_string()
150 | |> should.equal("FFAFF3FF")
151 | }
152 |
153 | pub fn pad_rgba_hex_string_test() {
154 | let assert Ok(c) = colour.from_rgba(r: 0.0, g: 1.0, b: 0.0, a: 1.0)
155 |
156 | c
157 | |> colour.to_rgba_hex_string()
158 | |> should.equal("00FF00FF")
159 |
160 | let assert Ok(c) = colour.from_rgba(r: 0.0, g: 0.0, b: 0.0, a: 1.0)
161 |
162 | c
163 | |> colour.to_rgba_hex_string()
164 | |> should.equal("000000FF")
165 | }
166 |
167 | pub fn to_rgb_hex_string_test() {
168 | let assert Ok(c) = colour.from_rgba(r: 1.0, g: 1.0, b: 1.0, a: 1.0)
169 |
170 | c
171 | |> colour.to_rgb_hex_string()
172 | |> should.equal("FFFFFF")
173 |
174 | let assert Ok(c) = colour.from_rgba(r: 1.0, g: 0.0, b: 0.0, a: 1.0)
175 |
176 | c
177 | |> colour.to_rgb_hex_string()
178 | |> should.equal("FF0000")
179 |
180 | colour.pink
181 | |> colour.to_rgb_hex_string()
182 | |> should.equal("FFAFF3")
183 | }
184 |
185 | pub fn pad_rgb_hex_string_test() {
186 | let assert Ok(c) = colour.from_rgb(r: 0.0, g: 1.0, b: 0.0)
187 |
188 | c
189 | |> colour.to_rgb_hex_string()
190 | |> should.equal("00FF00")
191 |
192 | let assert Ok(c) = colour.from_rgb(r: 0.0, g: 0.0, b: 0.0)
193 |
194 | c
195 | |> colour.to_rgb_hex_string()
196 | |> should.equal("000000")
197 | }
198 |
199 | pub fn to_rgba_hex_test() {
200 | let assert Ok(c) = colour.from_rgba(r: 1.0, g: 1.0, b: 1.0, a: 1.0)
201 |
202 | c
203 | |> colour.to_rgba_hex()
204 | |> should.equal(0xFFFFFFFF)
205 |
206 | let assert Ok(c) = colour.from_rgba(r: 1.0, g: 0.0, b: 0.0, a: 1.0)
207 |
208 | c
209 | |> colour.to_rgba_hex()
210 | |> should.equal(0xFF0000FF)
211 |
212 | colour.pink
213 | |> colour.to_rgba_hex()
214 | |> should.equal(0xFFAFF3FF)
215 | }
216 |
217 | pub fn to_rgb_hex_test() {
218 | let assert Ok(c) = colour.from_rgba(r: 1.0, g: 1.0, b: 1.0, a: 1.0)
219 |
220 | c
221 | |> colour.to_rgb_hex()
222 | |> should.equal(0xFFFFFF)
223 |
224 | let assert Ok(c) = colour.from_rgba(r: 1.0, g: 0.0, b: 0.0, a: 1.0)
225 |
226 | c
227 | |> colour.to_rgb_hex()
228 | |> should.equal(0xFF0000)
229 |
230 | colour.pink
231 | |> colour.to_rgb_hex()
232 | |> should.equal(0xFFAFF3)
233 | }
234 |
235 | pub fn json_identiy_test() {
236 | let assert Ok(c) = colour.from_rgba(r: 1.0, g: 1.0, b: 1.0, a: 1.0)
237 |
238 | c
239 | |> colour.encode
240 | |> json.to_string
241 | |> json.parse(colour.decoder())
242 | |> should.equal(Ok(c))
243 | }
244 |
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | Copyright 2023 Gleam Community Contributors
179 |
180 | Licensed under the Apache License, Version 2.0 (the "License");
181 | you may not use this file except in compliance with the License.
182 | You may obtain a copy of the License at
183 |
184 | http://www.apache.org/licenses/LICENSE-2.0
185 |
186 | Unless required by applicable law or agreed to in writing, software
187 | distributed under the License is distributed on an "AS IS" BASIS,
188 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
189 | See the License for the specific language governing permissions and
190 | limitations under the License.
--------------------------------------------------------------------------------
/src/gleam_community/colour.gleam:
--------------------------------------------------------------------------------
1 | ////
2 | //// - **Types**
3 | //// - [`Colour`](#Colour)
4 | //// - [`Color`](#Color)
5 | //// - **Constructors**
6 | //// - [`from_rgb255`](#from_rgb255)
7 | //// - [`from_rgb`](#from_rgb)
8 | //// - [`from_rgba`](#from_rgba)
9 | //// - [`from_hsl`](#from_hsl)
10 | //// - [`from_hsla`](#from_hsla)
11 | //// - [`from_rgb_hex`](#from_rgb_hex)
12 | //// - [`from_rgba_hex`](#from_rgba_hex)
13 | //// - [`from_rgb_hex_string`](#from_rgb_hex_string)
14 | //// - [`from_rgba_hex_string`](#from_rgba_hex_string)
15 | //// - **Conversions**
16 | //// - [`to_rgba`](#to_rgba)
17 | //// - [`to_hsla`](#hsla)
18 | //// - [`to_css_rgba_string`](#to_css_rgba_string)
19 | //// - [`to_rgba_hex_string`](#to_rgba_hex_string)
20 | //// - [`to_rgb_hex_string`](#to_rgb_hex_string)
21 | //// - [`to_rgba_hex`](#to_rgba_hex)
22 | //// - [`to_rgb_hex`](#to_rgb_hex)
23 | //// - **JSON**
24 | //// - [`encode`](#encode)
25 | //// - [`decoder`](#decoder)
26 | //// - **Colours**
27 | //// - [`light_red`](#light_red)
28 | //// - [`red`](#red)
29 | //// - [`dark_red`](#dark_red)
30 | //// - [`light_orange`](#light_orange)
31 | //// - [`orange`](#orange)
32 | //// - [`dark_orange`](#dark_orange)
33 | //// - [`light_yellow`](#light_yellow)
34 | //// - [`yellow`](#yellow)
35 | //// - [`dark_yellow`](#dark_yellow)
36 | //// - [`light_green`](#light_green)
37 | //// - [`green`](#green)
38 | //// - [`dark_green`](#dark_green)
39 | //// - [`light_blue`](#light_blue)
40 | //// - [`blue`](#blue)
41 | //// - [`dark_blue`](#dark_blue)
42 | //// - [`light_purple`](#light_purple)
43 | //// - [`purple`](#purple)
44 | //// - [`dark_purple`](#dark_purple)
45 | //// - [`light_brown`](#light_brown)
46 | //// - [`brown`](#brown)
47 | //// - [`dark_brown`](#dark_brown)
48 | //// - [`black`](#black)
49 | //// - [`white`](#white)
50 | //// - [`light_grey`](#light_grey)
51 | //// - [`grey`](#grey)
52 | //// - [`dark_grey`](#dark_grey)
53 | //// - [`light_gray`](#light_gray)
54 | //// - [`gray`](#gray)
55 | //// - [`dark_gray`](#dark_gray)
56 | //// - [`light_charcoal`](#light_charcoal)
57 | //// - [`charcoal`](#charcoal)
58 | //// - [`dark_charcoal`](#dark_charcoal)
59 | //// - [`pink`](#pink)
60 | ////
61 | //// ---
62 | ////
63 | //// This package was heavily inspired by the `elm-color` module.
64 | //// The original source code can be found
65 | //// here.
66 | ////
67 | ////
68 | //// The license of that package is produced below:
69 | ////
70 | ////
71 | //// > MIT License
72 | ////
73 | //// > Copyright 2018 Aaron VonderHaar
74 | ////
75 | //// > Redistribution and use in source and binary forms, with or without modification,
76 | //// are permitted provided that the following conditions are met:
77 | ////
78 | //// 1. Redistributions of source code must retain the above copyright notice,
79 | //// this list of conditions and the following disclaimer.
80 | ////
81 | //// 2. Redistributions in binary form must reproduce the above copyright notice,
82 | //// this list of conditions and the following disclaimer in the documentation
83 | //// and/or other materials provided with the distribution.
84 | ////
85 | //// 3. Neither the name of the copyright holder nor the names of its contributors
86 | //// may be used to endorse or promote products derived from this software without
87 | //// specific prior written permission.
88 | ////
89 | //// > THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
90 | //// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
91 | //// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
92 | //// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
93 | //// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
94 | //// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
95 | //// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
96 | //// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
97 | ////
98 | //// > The above copyright notice and this permission notice shall be included in all
99 | //// copies or substantial portions of the Software.
100 | ////
101 | ////
102 |
103 | // Just in case we decide in the future to no longer include the above reference
104 | // and license, this package was initially a port of the `elm-color` module:
105 | //
106 | // https://github.com/avh4/elm-color/
107 | //
108 |
109 | // IMPORTS --------------------------------------------------------------------
110 |
111 | import gleam/dynamic/decode
112 | import gleam/float
113 | import gleam/int
114 | import gleam/json.{type Json}
115 | import gleam/list
116 | import gleam/result
117 | import gleam/string
118 |
119 | // TYPES ----------------------------------------------------------------------
120 |
121 | /// A representation of a colour that can be converted to RGBA or HSLA format.
122 | ///
123 | ///
131 | ///
132 | ///
133 | pub opaque type Colour {
134 | Rgba(r: Float, g: Float, b: Float, a: Float)
135 | Hsla(h: Float, s: Float, l: Float, a: Float)
136 | }
137 |
138 | /// Type alias for `Colour`
139 | ///
140 | ///
148 | ///
149 | ///
150 | pub type Color =
151 | Colour
152 |
153 | // UTILITY --------------------------------------------------------------------
154 |
155 | fn valid_colour_value(c: Float) -> Result(Float, Nil) {
156 | case c >. 1.0 || c <. 0.0 {
157 | True -> Error(Nil)
158 | False -> Ok(c)
159 | }
160 | }
161 |
162 | fn hue_to_rgb(hue: Float, m1: Float, m2: Float) -> Float {
163 | let h = case hue {
164 | _ if hue <. 0.0 -> hue +. 1.0
165 | _ if hue >. 1.0 -> hue -. 1.0
166 | _ -> hue
167 | }
168 |
169 | let h_t_6 = h *. 6.0
170 | let h_t_2 = h *. 2.0
171 | let h_t_3 = h *. 3.0
172 |
173 | case h {
174 | _ if h_t_6 <. 1.0 -> m1 +. { m2 -. m1 } *. h *. 6.0
175 | _ if h_t_2 <. 1.0 -> m2
176 | _ if h_t_3 <. 2.0 -> m1 +. { m2 -. m1 } *. { 2.0 /. 3.0 -. h } *. 6.0
177 | _ -> m1
178 | }
179 | }
180 |
181 | fn hex_string_to_int(hex_string: String) -> Result(Int, Nil) {
182 | let hex = case hex_string {
183 | "#" <> hex_number -> hex_number
184 | "0x" <> hex_number -> hex_number
185 | _ -> hex_string
186 | }
187 |
188 | hex
189 | |> string.lowercase()
190 | |> string.to_graphemes()
191 | |> list.reverse()
192 | |> list.index_fold(Ok(0), fn(total, char, index) {
193 | case total {
194 | Error(Nil) -> Error(Nil)
195 | Ok(v) -> {
196 | use num <- result.try(case char {
197 | "a" -> Ok(10)
198 | "b" -> Ok(11)
199 | "c" -> Ok(12)
200 | "d" -> Ok(13)
201 | "e" -> Ok(14)
202 | "f" -> Ok(15)
203 | _ -> int.parse(char)
204 | })
205 | use base <- result.try(int.power(16, int.to_float(index)))
206 | Ok(v + float.round(int.to_float(num) *. base))
207 | }
208 | }
209 | })
210 | }
211 |
212 | fn hsla_to_rgba(
213 | h: Float,
214 | s: Float,
215 | l: Float,
216 | a: Float,
217 | ) -> #(Float, Float, Float, Float) {
218 | let m2 = case l <=. 0.5 {
219 | True -> l *. { s +. 1.0 }
220 | False -> l +. s -. l *. s
221 | }
222 |
223 | let m1 = l *. 2.0 -. m2
224 |
225 | let r = hue_to_rgb(h +. 1.0 /. 3.0, m1, m2)
226 | let g = hue_to_rgb(h, m1, m2)
227 | let b = hue_to_rgb(h -. 1.0 /. 3.0, m1, m2)
228 |
229 | #(r, g, b, a)
230 | }
231 |
232 | fn rgba_to_hsla(
233 | r: Float,
234 | g: Float,
235 | b: Float,
236 | a: Float,
237 | ) -> #(Float, Float, Float, Float) {
238 | let min_colour = float.min(r, float.min(g, b))
239 |
240 | let max_colour = float.max(r, float.max(g, b))
241 |
242 | let h1 = case True {
243 | _ if max_colour == r -> float.divide(g -. b, max_colour -. min_colour)
244 | _ if max_colour == g ->
245 | float.divide(b -. r, max_colour -. min_colour)
246 | |> result.try(fn(d) { Ok(2.0 +. d) })
247 | _ ->
248 | float.divide(r -. g, max_colour -. min_colour)
249 | |> result.try(fn(d) { Ok(4.0 +. d) })
250 | }
251 |
252 | let h2 = case h1 {
253 | Ok(v) -> Ok(v *. { 1.0 /. 6.0 })
254 | _ -> h1
255 | }
256 |
257 | let h3 = case h2 {
258 | Ok(v) if v <. 0.0 -> v +. 1.0
259 | Ok(v) -> v
260 | _ -> 0.0
261 | }
262 |
263 | let l = { min_colour +. max_colour } /. 2.0
264 |
265 | let s = case True {
266 | _ if min_colour == max_colour -> 0.0
267 | _ if l <. 0.5 ->
268 | { max_colour -. min_colour } /. { max_colour +. min_colour }
269 | _ -> { max_colour -. min_colour } /. { 2.0 -. max_colour -. min_colour }
270 | }
271 |
272 | #(h3, s, l, a)
273 | }
274 |
275 | // CONSTRUCTORS ---------------------------------------------------------------
276 |
277 | /// Returns a `Result(Colour)` created from the given 8 bit RGB values.
278 | ///
279 | /// Returns `Error(Nil)` if the supplied RGB values are greater than 255 or less than 0.
280 | ///
281 | ///
282 | /// Example:
283 | ///
284 | /// ```gleam
285 | /// fn example() {
286 | /// assert Ok(red) = from_rgb255(255, 0, 0)
287 | /// }
288 | /// ```
289 | ///
290 | ///
291 | ///
299 | ///
300 | pub fn from_rgb255(r red: Int, g green: Int, b blue: Int) -> Result(Colour, Nil) {
301 | use r <- result.try(
302 | red
303 | |> int.to_float()
304 | |> float.divide(255.0)
305 | |> result.try(valid_colour_value),
306 | )
307 |
308 | use g <- result.try(
309 | green
310 | |> int.to_float()
311 | |> float.divide(255.0)
312 | |> result.try(valid_colour_value),
313 | )
314 |
315 | use b <- result.try(
316 | blue
317 | |> int.to_float()
318 | |> float.divide(255.0)
319 | |> result.try(valid_colour_value),
320 | )
321 |
322 | Ok(Rgba(r: r, g: g, b: b, a: 1.0))
323 | }
324 |
325 | /// Returns `Result(Colour)` created from the given RGB values.
326 | ///
327 | /// If the supplied RGB values are greater than 1.0 or less than 0.0 returns `Error(Nil)`
328 | ///
329 | ///
330 | /// Example:
331 | ///
332 | /// ```gleam
333 | /// fn example() {
334 | /// assert Ok(red) = from_rgb(1.0, 0.0, 0.0)
335 | /// }
336 | /// ```
337 | ///
338 | ///
339 | ///
347 | ///
348 | pub fn from_rgb(
349 | r red: Float,
350 | g green: Float,
351 | b blue: Float,
352 | ) -> Result(Colour, Nil) {
353 | use r <- result.try(valid_colour_value(red))
354 | use g <- result.try(valid_colour_value(green))
355 | use b <- result.try(valid_colour_value(blue))
356 |
357 | Ok(Rgba(r: r, g: g, b: b, a: 1.0))
358 | }
359 |
360 | /// Returns `Result(Colour)` created from the given RGBA values.
361 | ///
362 | /// Returns `Error(Nil)` if the supplied RGBA values are greater than 1.0 or less than 0.0.
363 | ///
364 | ///
365 | /// Example:
366 | ///
367 | /// ```gleam
368 | /// fn example() {
369 | /// assert Ok(red_half_opacity) = from_rbga(1.0, 0.0, 0.0, 0.5)
370 | /// }
371 | /// ```
372 | ///
373 | ///
374 | ///
382 | ///
383 | pub fn from_rgba(
384 | r red: Float,
385 | g green: Float,
386 | b blue: Float,
387 | a alpha: Float,
388 | ) -> Result(Colour, Nil) {
389 | use r <- result.try(valid_colour_value(red))
390 | use g <- result.try(valid_colour_value(green))
391 | use b <- result.try(valid_colour_value(blue))
392 | use a <- result.try(valid_colour_value(alpha))
393 |
394 | Ok(Rgba(r: r, g: g, b: b, a: a))
395 | }
396 |
397 | /// Returns `Result(Colour)` created from the given HSLA values.
398 | ///
399 | /// Returns `Error(Nil)`f the supplied HSLA values are greater than 1.0 or less than 0.0.
400 | ///
401 | ///
402 | /// Example:
403 | ///
404 | /// ```gleam
405 | /// fn example() {
406 | /// assert Ok(red_half_opacity) = from_hsla(0.0, 1.0, 0.5, 0.5)
407 | /// }
408 | /// ```
409 | ///
410 | ///
411 | ///
419 | ///
420 | pub fn from_hsla(
421 | h hue: Float,
422 | s saturation: Float,
423 | l lightness: Float,
424 | a alpha: Float,
425 | ) -> Result(Colour, Nil) {
426 | use h <- result.try(valid_colour_value(hue))
427 | use s <- result.try(valid_colour_value(saturation))
428 | use l <- result.try(valid_colour_value(lightness))
429 | use a <- result.try(valid_colour_value(alpha))
430 |
431 | Ok(Hsla(h: h, s: s, l: l, a: a))
432 | }
433 |
434 | /// Returns `Result(Colour)` created from the given HSL values.
435 | ///
436 | /// Returns `Error(Nil)` if the supplied HSL values are greater than 1.0 or less than 0.0.
437 | ///
438 | ///
439 | /// Example:
440 | ///
441 | /// ```gleam
442 | /// fn example() {
443 | /// assert Ok(red) = from_hsla(0.0, 1.0, 0.5)
444 | /// }
445 | /// ```
446 | ///
447 | ///
448 | ///
456 | ///
457 | pub fn from_hsl(
458 | h hue: Float,
459 | s saturation: Float,
460 | l lightness: Float,
461 | ) -> Result(Colour, Nil) {
462 | from_hsla(hue, saturation, lightness, 1.0)
463 | }
464 |
465 | /// Returns a `Result(Colour)` created from the given hex `Int`.
466 | ///
467 | /// Returns `Error(Nil)` if the supplied hex `Int is greater than 0xffffff or less than 0x0.
468 | ///
469 | ///
470 | /// Example:
471 | ///
472 | /// ```gleam
473 | /// fn example() {
474 | /// assert Ok(red) = from_rgb_hex(0xff0000)
475 | /// }
476 | /// ```
477 | ///
478 | ///
479 | ///
487 | ///
488 | pub fn from_rgb_hex(hex: Int) -> Result(Colour, Nil) {
489 | case hex > 0xffffff || hex < 0 {
490 | True -> Error(Nil)
491 | False -> {
492 | let r =
493 | int.bitwise_shift_right(hex, 16)
494 | |> int.bitwise_and(0xff)
495 | let g =
496 | int.bitwise_shift_right(hex, 8)
497 | |> int.bitwise_and(0xff)
498 | let b = int.bitwise_and(hex, 0xff)
499 | from_rgb255(r, g, b)
500 | }
501 | }
502 | }
503 |
504 | /// Returns a `Result(Colour)` created from the given RGB hex `String`.
505 | ///
506 | /// Returns `Error(Nil)` if the supplied hex `String` is invalid, or greater than `"#ffffff" or less than `"#0"`
507 | ///
508 | ///
509 | /// Example:
510 | ///
511 | /// ```gleam
512 | /// fn example() {
513 | /// assert Ok(red) = from_rgb_hex_string("#ff0000")
514 | /// }
515 | /// ```
516 | ///
517 | ///
518 | ///
526 | ///
527 | pub fn from_rgb_hex_string(hex_string: String) -> Result(Colour, Nil) {
528 | use hex_int <- result.try(hex_string_to_int(hex_string))
529 |
530 | from_rgb_hex(hex_int)
531 | }
532 |
533 | /// Returns a `Result(Colour)` created from the given RGBA hex `String`.
534 | ///
535 | /// Returns `Error(Nil)` if the supplied hex `String` is invalid, or greater than `"#ffffffff" or less than `"#0"`
536 | ///
537 | ///
538 | /// Example:
539 | ///
540 | /// ```gleam
541 | /// fn example() {
542 | /// assert Ok(red_half_opacity) = from_rgba_hex_string("#ff00007f")
543 | /// }
544 | /// ```
545 | ///
546 | ///
547 | ///
555 | ///
556 | pub fn from_rgba_hex_string(hex_string: String) -> Result(Colour, Nil) {
557 | use hex_int <- result.try(hex_string_to_int(hex_string))
558 |
559 | from_rgba_hex(hex_int)
560 | }
561 |
562 | /// Returns a `Result(Colour)` created from the given hex `Int`.
563 | ///
564 | /// Returns `Error(Nil)` if the supplied hex `Int is greater than 0xffffffff or less than 0x0.
565 | ///
566 | ///
567 | /// Example:
568 | ///
569 | /// ```gleam
570 | /// fn example() {
571 | /// assert Ok(red_half_opacity) = from_rgba_hex(0xff00007f)
572 | /// }
573 | /// ```
574 | ///
575 | ///
576 | ///
584 | ///
585 | pub fn from_rgba_hex(hex: Int) -> Result(Colour, Nil) {
586 | case hex > 0xffffffff || hex < 0 {
587 | True -> Error(Nil)
588 | False -> {
589 | // This won't fail because we are always dividing by 255.0
590 | let assert Ok(r) =
591 | int.bitwise_shift_right(hex, 24)
592 | |> int.bitwise_and(0xff)
593 | |> int.to_float()
594 | |> float.divide(255.0)
595 | // This won't fail because we are always dividing by 255.0
596 | let assert Ok(g) =
597 | int.bitwise_shift_right(hex, 16)
598 | |> int.bitwise_and(0xff)
599 | |> int.to_float()
600 | |> float.divide(255.0)
601 | // This won't fail because we are always dividing by 255.0
602 | let assert Ok(b) =
603 | int.bitwise_shift_right(hex, 8)
604 | |> int.bitwise_and(0xff)
605 | |> int.to_float()
606 | |> float.divide(255.0)
607 | // This won't fail because we are always dividing by 255.0
608 | let assert Ok(a) =
609 | int.bitwise_and(hex, 0xff)
610 | |> int.to_float()
611 | |> float.divide(255.0)
612 | from_rgba(r, g, b, a)
613 | }
614 | }
615 | }
616 |
617 | // CONVERSIONS ----------------------------------------------------------------
618 |
619 | /// Returns `#(Float, Float, Float, Float)` representing the given `Colour`'s
620 | /// R, G, B, and A values respectively.
621 | ///
622 | ///
623 | /// Example:
624 | ///
625 | /// ```gleam
626 | /// fn example() {
627 | /// assert Ok(red) = from_rgb255(255, 0, 0)
628 | /// let #(r, g, b, a) = to_rgba(red)
629 | /// }
630 | /// ```
631 | ///
632 | ///
633 | ///
641 | ///
642 | pub fn to_rgba(colour: Colour) -> #(Float, Float, Float, Float) {
643 | case colour {
644 | Rgba(r, g, b, a) -> #(r, g, b, a)
645 | Hsla(h, s, l, a) -> hsla_to_rgba(h, s, l, a)
646 | }
647 | }
648 |
649 | /// Returns `#(Float, Float, Float, Float)` representing the given `Colour`'s
650 | /// H, S, L, and A values respectively.
651 | ///
652 | ///
653 | /// Example:
654 | ///
655 | /// ```gleam
656 | /// fn example() {
657 | /// assert Ok(red) = from_rgb255(255, 0, 0)
658 | /// let #(h, s, l, a) = to_hsla(red)
659 | /// }
660 | /// ```
661 | ///
662 | ///
663 | ///
671 | ///
672 | pub fn to_hsla(colour: Colour) -> #(Float, Float, Float, Float) {
673 | case colour {
674 | Hsla(h, s, l, a) -> #(h, s, l, a)
675 | Rgba(r, g, b, a) -> rgba_to_hsla(r, g, b, a)
676 | }
677 | }
678 |
679 | /// Returns an rgba formatted CSS `String` created from the given `Colour`.
680 | ///
681 | ///
682 | /// Example:
683 | ///
684 | /// ```gleam
685 | /// fn example() {
686 | /// assert Ok(red) = from_rgb255(255, 0, 0)
687 | /// let css_red = to_css_rgba_string(red)
688 | /// }
689 | /// ```
690 | ///
691 | ///
692 | ///
700 | ///
701 | pub fn to_css_rgba_string(colour: Colour) -> String {
702 | let #(r, g, b, a) = to_rgba(colour)
703 |
704 | let percent = fn(x: Float) -> Float {
705 | // This won't fail because we are always dividing by 100.0
706 | let assert Ok(p) =
707 | x
708 | |> float.multiply(10_000.0)
709 | |> float.round()
710 | |> int.to_float()
711 | |> float.divide(100.0)
712 |
713 | p
714 | }
715 |
716 | let round_to = fn(x: Float) -> Float {
717 | // This won't fail because we are always dividing by 1000.0
718 | let assert Ok(r) =
719 | x
720 | |> float.multiply(1000.0)
721 | |> float.round()
722 | |> int.to_float()
723 | |> float.divide(1000.0)
724 |
725 | r
726 | }
727 |
728 | string.join(
729 | [
730 | "rgba(",
731 | float.to_string(percent(r)) <> "%,",
732 | float.to_string(percent(g)) <> "%,",
733 | float.to_string(percent(b)) <> "%,",
734 | float.to_string(round_to(a)),
735 | ")",
736 | ],
737 | "",
738 | )
739 | }
740 |
741 | /// Returns an rgba hex formatted `String` created from the given `Colour`.
742 | ///
743 | ///
744 | /// Example:
745 | ///
746 | /// ```gleam
747 | /// fn example() {
748 | /// assert Ok(red) = from_rgba(1.0, 0.0, 0.0, 1.0)
749 | /// let red_hex = to_rgba_hex_string(red)
750 | /// }
751 | /// ```
752 | ///
753 | ///
754 | ///
762 | ///
763 | pub fn to_rgba_hex_string(colour: Colour) -> String {
764 | let hex_string =
765 | to_rgba_hex(colour)
766 | |> int.to_base16()
767 |
768 | case string.length(hex_string) {
769 | 8 -> hex_string
770 | l -> string.repeat("0", 8 - l) <> hex_string
771 | }
772 | }
773 |
774 | /// Returns an rgb hex formatted `String` created from the given `Colour`.
775 | ///
776 | ///
777 | /// Example:
778 | ///
779 | /// ```gleam
780 | /// fn example() {
781 | /// assert Ok(red) = from_rgba(255, 0, 0)
782 | /// let red_hex = to_rgb_hex_string(red)
783 | /// }
784 | /// ```
785 | ///
786 | ///
787 | ///
795 | ///
796 | pub fn to_rgb_hex_string(colour: Colour) -> String {
797 | let hex_string =
798 | to_rgb_hex(colour)
799 | |> int.to_base16()
800 |
801 | case string.length(hex_string) {
802 | 6 -> hex_string
803 | l -> string.repeat("0", 6 - l) <> hex_string
804 | }
805 | }
806 |
807 | /// Returns an hex `Int` created from the given `Colour`.
808 | ///
809 | ///
810 | /// Example:
811 | ///
812 | /// ```gleam
813 | /// fn example() {
814 | /// assert Ok(red) = from_rgba(1.0, 0.0, 0.0, 1.0)
815 | /// let red_hex_int = to_rgba_hex(red)
816 | /// }
817 | /// ```
818 | ///
819 | ///
820 | ///
828 | ///
829 | pub fn to_rgba_hex(colour: Colour) -> Int {
830 | let #(r, g, b, a) = to_rgba(colour)
831 |
832 | let red =
833 | r *. 255.0
834 | |> float.round()
835 | |> int.bitwise_shift_left(24)
836 |
837 | let green =
838 | g *. 255.0
839 | |> float.round()
840 | |> int.bitwise_shift_left(16)
841 |
842 | let blue =
843 | b *. 255.0
844 | |> float.round()
845 | |> int.bitwise_shift_left(8)
846 |
847 | let alpha =
848 | a *. 255.0
849 | |> float.round()
850 |
851 | red + green + blue + alpha
852 | }
853 |
854 | /// Returns a rgb hex `Int` created from the given `Colour`.
855 | ///
856 | ///
857 | /// Example:
858 | ///
859 | /// ```gleam
860 | /// fn example() {
861 | /// assert Ok(red) = from_rgba(255, 0, 0)
862 | /// let red_hex_int = to_rgb_hex(red)
863 | /// }
864 | /// ```
865 | ///
866 | ///
867 | ///
875 | ///
876 | pub fn to_rgb_hex(colour: Colour) -> Int {
877 | let #(r, g, b, _) = to_rgba(colour)
878 |
879 | let red =
880 | r *. 255.0
881 | |> float.round()
882 | |> int.bitwise_shift_left(16)
883 |
884 | let green =
885 | g *. 255.0
886 | |> float.round()
887 | |> int.bitwise_shift_left(8)
888 |
889 | let blue =
890 | b *. 255.0
891 | |> float.round()
892 |
893 | red + green + blue
894 | }
895 |
896 | // JSON ------------------------------------------------------------------------
897 |
898 | /// Encodes a `Colour` value as a Gleam [`Json`](https://hexdocs.pm/gleam_json/gleam/json.html#Json)
899 | /// value. You'll need this if you want to send a `Colour` value over the network
900 | /// in a HTTP request or response, for example.
901 | ///
902 | ///
910 | ///
911 | pub fn encode(colour: Colour) -> Json {
912 | case colour {
913 | Rgba(r, g, b, a) -> encode_rgba(r, g, b, a)
914 | Hsla(h, s, l, a) -> encode_hsla(h, s, l, a)
915 | }
916 | }
917 |
918 | fn encode_rgba(r: Float, g: Float, b: Float, a: Float) -> Json {
919 | json.object([
920 | #("r", json.float(r)),
921 | #("g", json.float(g)),
922 | #("b", json.float(b)),
923 | #("a", json.float(a)),
924 | ])
925 | }
926 |
927 | fn encode_hsla(h: Float, s: Float, l: Float, a: Float) -> Json {
928 | json.object([
929 | #("h", json.float(h)),
930 | #("s", json.float(s)),
931 | #("l", json.float(l)),
932 | #("a", json.float(a)),
933 | ])
934 | }
935 |
936 | /// Attempt to decode some [`Dynamic`](https://hexdocs.pm/gleam_stdlib/gleam/dynamic.html#Dynamic)
937 | /// value into a `Colour`. Most often you'll use this to decode some JSON.
938 | ///
939 | ///
947 | ///
948 | pub fn decoder() -> decode.Decoder(Colour) {
949 | decode.one_of(rgba_decoder(), or: [hsla_decoder()])
950 | }
951 |
952 | fn rgba_decoder() -> decode.Decoder(Colour) {
953 | use r <- decode.field("r", decode.float)
954 | use g <- decode.field("g", decode.float)
955 | use b <- decode.field("b", decode.float)
956 | use a <- decode.field("a", decode.float)
957 |
958 | decode.success(Rgba(r, g, b, a))
959 | }
960 |
961 | fn hsla_decoder() -> decode.Decoder(Colour) {
962 | use h <- decode.field("h", decode.float)
963 | use s <- decode.field("s", decode.float)
964 | use l <- decode.field("l", decode.float)
965 | use a <- decode.field("a", decode.float)
966 |
967 | decode.success(Hsla(h, s, l, a))
968 | }
969 |
970 | // COLOURS ---------------------------------------------------------------------
971 |
972 | /// A `Colour` reprsenting the colour RGBA(239, 41, 41, 1.0)
973 | pub const light_red = Rgba(
974 | r: 0.9372549019607843,
975 | g: 0.1607843137254902,
976 | b: 0.1607843137254902,
977 | a: 1.0,
978 | )
979 |
980 | /// A `Colour` reprsenting the colour RGBA(204, 0, 0, 1.0)
981 | pub const red = Rgba(r: 0.8, g: 0.0, b: 0.0, a: 1.0)
982 |
983 | /// A `Colour` reprsenting the colour RGBA(164, 0, 0, 1.0)
984 | pub const dark_red = Rgba(r: 0.6431372549019608, g: 0.0, b: 0.0, a: 1.0)
985 |
986 | /// A `Colour` reprsenting the colour RGBA(252, 175, 62, 1.0)
987 | pub const light_orange = Rgba(
988 | r: 0.9882352941176471,
989 | g: 0.6862745098039216,
990 | b: 0.24313725490196078,
991 | a: 1.0,
992 | )
993 |
994 | /// A `Colour` reprsenting the colour RGBA(245, 121, 0, 1.0)
995 | pub const orange = Rgba(
996 | r: 0.9607843137254902,
997 | g: 0.4745098039215686,
998 | b: 0.0,
999 | a: 1.0,
1000 | )
1001 |
1002 | /// A `Colour` reprsenting the colour RGBA(206, 92, 0, 1.0)
1003 | pub const dark_orange = Rgba(
1004 | r: 0.807843137254902,
1005 | g: 0.3607843137254902,
1006 | b: 0.0,
1007 | a: 1.0,
1008 | )
1009 |
1010 | /// A `Colour` reprsenting the colour RGBA(255, 233, 79, 1.0)
1011 | pub const light_yellow = Rgba(
1012 | r: 1.0,
1013 | g: 0.9137254901960784,
1014 | b: 0.30980392156862746,
1015 | a: 1.0,
1016 | )
1017 |
1018 | /// A `Colour` reprsenting the colour RGBA(237, 212, 0, 1.0)
1019 | pub const yellow = Rgba(
1020 | r: 0.9294117647058824,
1021 | g: 0.8313725490196079,
1022 | b: 0.0,
1023 | a: 1.0,
1024 | )
1025 |
1026 | /// A `Colour` reprsenting the colour RGBA(196, 160, 0, 1.0)
1027 | pub const dark_yellow = Rgba(
1028 | r: 0.7686274509803922,
1029 | g: 0.6274509803921569,
1030 | b: 0.0,
1031 | a: 1.0,
1032 | )
1033 |
1034 | /// A `Colour` reprsenting the colour RGBA(138, 226, 52, 1.0)
1035 | pub const light_green = Rgba(
1036 | r: 0.5411764705882353,
1037 | g: 0.8862745098039215,
1038 | b: 0.20392156862745098,
1039 | a: 1.0,
1040 | )
1041 |
1042 | /// A `Colour` reprsenting the colour RGBA(115, 210, 22, 1.0)
1043 | pub const green = Rgba(
1044 | r: 0.45098039215686275,
1045 | g: 0.8235294117647058,
1046 | b: 0.08627450980392157,
1047 | a: 1.0,
1048 | )
1049 |
1050 | /// A `Colour` reprsenting the colour RGBA(78, 154, 6, 1.0)
1051 | pub const dark_green = Rgba(
1052 | r: 0.3058823529411765,
1053 | g: 0.6039215686274509,
1054 | b: 0.023529411764705882,
1055 | a: 1.0,
1056 | )
1057 |
1058 | /// A `Colour` reprsenting the colour RGBA(114, 159, 207, 1.0)
1059 | pub const light_blue = Rgba(
1060 | r: 0.4470588235294118,
1061 | g: 0.6235294117647059,
1062 | b: 0.8117647058823529,
1063 | a: 1.0,
1064 | )
1065 |
1066 | /// A `Colour` reprsenting the colour RGBA(52, 101, 164, 1.0)
1067 | pub const blue = Rgba(
1068 | r: 0.20392156862745098,
1069 | g: 0.396078431372549,
1070 | b: 0.6431372549019608,
1071 | a: 1.0,
1072 | )
1073 |
1074 | /// A `Colour` reprsenting the colour RGBA(32, 74, 135, 1.0)
1075 | pub const dark_blue = Rgba(
1076 | r: 0.12549019607843137,
1077 | g: 0.2901960784313726,
1078 | b: 0.5294117647058824,
1079 | a: 1.0,
1080 | )
1081 |
1082 | /// A `Colour` reprsenting the colour RGBA(173, 127, 168, 1.0)
1083 | pub const light_purple = Rgba(
1084 | r: 0.6784313725490196,
1085 | g: 0.4980392156862745,
1086 | b: 0.6588235294117647,
1087 | a: 1.0,
1088 | )
1089 |
1090 | /// A `Colour` reprsenting the colour RGBA(117, 80, 123, 1.0)
1091 | pub const purple = Rgba(
1092 | r: 0.4588235294117647,
1093 | g: 0.3137254901960784,
1094 | b: 0.4823529411764706,
1095 | a: 1.0,
1096 | )
1097 |
1098 | /// A `Colour` reprsenting the colour RGBA(92, 53, 102, 1.0)
1099 | pub const dark_purple = Rgba(
1100 | r: 0.3607843137254902,
1101 | g: 0.20784313725490197,
1102 | b: 0.4,
1103 | a: 1.0,
1104 | )
1105 |
1106 | /// A `Colour` reprsenting the colour RGBA(233, 185, 110, 1.0)
1107 | pub const light_brown = Rgba(
1108 | r: 0.9137254901960784,
1109 | g: 0.7254901960784313,
1110 | b: 0.43137254901960786,
1111 | a: 1.0,
1112 | )
1113 |
1114 | /// A `Colour` reprsenting the colour RGBA(193, 125, 17, 1.0)
1115 | pub const brown = Rgba(
1116 | r: 0.7568627450980392,
1117 | g: 0.49019607843137253,
1118 | b: 0.06666666666666667,
1119 | a: 1.0,
1120 | )
1121 |
1122 | /// A `Colour` reprsenting the colour RGBA(143, 89, 2, 1.0)
1123 | pub const dark_brown = Rgba(
1124 | r: 0.5607843137254902,
1125 | g: 0.34901960784313724,
1126 | b: 0.00784313725490196,
1127 | a: 1.0,
1128 | )
1129 |
1130 | /// A `Colour` reprsenting the colour RGBA(0, 0, 0, 1.0)
1131 | pub const black = Rgba(r: 0.0, g: 0.0, b: 0.0, a: 1.0)
1132 |
1133 | /// A `Colour` reprsenting the colour RGBA(255, 255, 255, 1.0)
1134 | pub const white = Rgba(r: 1.0, g: 1.0, b: 1.0, a: 1.0)
1135 |
1136 | /// A `Colour` reprsenting the colour RGBA(238, 238, 236, 1.0)
1137 | pub const light_grey = Rgba(
1138 | r: 0.9333333333333333,
1139 | g: 0.9333333333333333,
1140 | b: 0.9254901960784314,
1141 | a: 1.0,
1142 | )
1143 |
1144 | /// A `Colour` reprsenting the colour RGBA(211, 215, 207, 1.0)
1145 | pub const grey = Rgba(
1146 | r: 0.8274509803921568,
1147 | g: 0.8431372549019608,
1148 | b: 0.8117647058823529,
1149 | a: 1.0,
1150 | )
1151 |
1152 | /// A `Colour` reprsenting the colour RGBA(186, 189, 182, 1.0)
1153 | pub const dark_grey = Rgba(
1154 | r: 0.7294117647058823,
1155 | g: 0.7411764705882353,
1156 | b: 0.7137254901960784,
1157 | a: 1.0,
1158 | )
1159 |
1160 | /// A `Colour` reprsenting the colour RGBA(238, 238, 236, 1.0)
1161 | pub const light_gray = Rgba(
1162 | r: 0.9333333333333333,
1163 | g: 0.9333333333333333,
1164 | b: 0.9254901960784314,
1165 | a: 1.0,
1166 | )
1167 |
1168 | /// A `Colour` reprsenting the colour RGBA(211, 215, 207, 1.0)
1169 | pub const gray = Rgba(
1170 | r: 0.8274509803921568,
1171 | g: 0.8431372549019608,
1172 | b: 0.8117647058823529,
1173 | a: 1.0,
1174 | )
1175 |
1176 | /// A `Colour` reprsenting the colour RGBA(186, 189, 182, 1.0)
1177 | pub const dark_gray = Rgba(
1178 | r: 0.7294117647058823,
1179 | g: 0.7411764705882353,
1180 | b: 0.7137254901960784,
1181 | a: 1.0,
1182 | )
1183 |
1184 | /// A `Colour` reprsenting the colour RGBA(136, 138, 133, 1.0)
1185 | pub const light_charcoal = Rgba(
1186 | r: 0.5333333333333333,
1187 | g: 0.5411764705882353,
1188 | b: 0.5215686274509804,
1189 | a: 1.0,
1190 | )
1191 |
1192 | /// A `Colour` reprsenting the colour RGBA(85, 87, 83, 1.0)
1193 | pub const charcoal = Rgba(
1194 | r: 0.3333333333333333,
1195 | g: 0.3411764705882353,
1196 | b: 0.3254901960784314,
1197 | a: 1.0,
1198 | )
1199 |
1200 | /// A `Colour` reprsenting the colour RGBA(46, 52, 54, 1.0)
1201 | pub const dark_charcoal = Rgba(
1202 | r: 0.1803921568627451,
1203 | g: 0.20392156862745098,
1204 | b: 0.21176470588235294,
1205 | a: 1.0,
1206 | )
1207 |
1208 | /// A `Colour` reprsenting the colour RGBA(255, 175, 243, 1.0)
1209 | pub const pink = Rgba(
1210 | r: 1.0,
1211 | g: 0.6862745098039216,
1212 | b: 0.9529411764705882,
1213 | a: 1.0,
1214 | )
1215 |
--------------------------------------------------------------------------------