From d5aff726c388ce00ed67cd88e6b44b00dbec7afe Mon Sep 17 00:00:00 2001 From: Ava Gaiety W Date: Tue, 16 Sep 2025 21:26:19 -0600 Subject: [PATCH] color pallete class, converter, tests --- .gitignore | 175 ++++++++++++++++++++++++++++++++++++ .mise.toml | 2 + README.md | 15 ++++ bun.lockb | Bin 0 -> 4515 bytes index.ts | 13 +++ jsconfig.json | 27 ++++++ package.json | 13 +++ src/helpers/color.ts | 75 ++++++++++++++++ src/palette.toml | 13 +++ src/palette.ts | 32 +++++++ tests/helpers/color.test.ts | 86 ++++++++++++++++++ 11 files changed, 451 insertions(+) create mode 100644 .gitignore create mode 100644 .mise.toml create mode 100644 README.md create mode 100755 bun.lockb create mode 100644 index.ts create mode 100644 jsconfig.json create mode 100644 package.json create mode 100644 src/helpers/color.ts create mode 100644 src/palette.toml create mode 100644 src/palette.ts create mode 100644 tests/helpers/color.test.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9b1ee42 --- /dev/null +++ b/.gitignore @@ -0,0 +1,175 @@ +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Caches + +.cache + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/.mise.toml b/.mise.toml new file mode 100644 index 0000000..a94d1ed --- /dev/null +++ b/.mise.toml @@ -0,0 +1,2 @@ +[tools] +bun = "latest" diff --git a/README.md b/README.md new file mode 100644 index 0000000..70da4f4 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# verdigris + +To install dependencies: + +```bash +bun install +``` + +To run: + +```bash +bun run index.js +``` + +This project was created using `bun init` in bun v1.1.13. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..a98e11a36adc584e60cad35fc25ba0b48b49eea9 GIT binary patch literal 4515 zcmd^C4Nz276n?vG#*c&|SrIrJ2pO_(e-K)VCCsSEB9r`?6cCmNZY;a(dk+#w<3}r< zu|!b(OUqTV{K*uxNzzPBF(m27upxyK(LhQfz%1;Xx9`HUsg>zxrl~Wtd+xpOeBV9y z+;iW%C-4q+4#%ofO%^p{$yKK2Slr>KGFy$A=_WIyO1H9EDZDCI<1VKtYF6>d{9D&m zUeLGBneA3uyz!Q0B_AA&^S!yvq7Q!eu3!@&r|!=WhG-{?ZIpG*vdz8ggq`a{bT zv>CKJXyfb@b0)M$h<#rfMWLMu^iX`xaeQ7jLnT2U$O=b+dpHg@0{-?Dy?*|F;h&`1 zMjK*0y!7?c-#ii5wXWskZ;CXJj-1(WY{*^jP1F>16|`p@f6==w!1v~$;YEQ7wUOQY zP>;N_(o*$Pi-UI=BHgS}AAbA9s=e!i3PZ;(U{Ybaza$t%lo0sc5B|DJ`6?(J_H)$0e>TaAa4gP1pf>)aQ|^` z%y}|C7$OB2;SYUAJc?A~ILQe-!Pv(+P*7tZ&ZrQYfQa$bNR}gFI~3g>uv2rLo&CxHRqo4e5bCrzVq3U z3-|X->`DG8>}b`cfM5x)bUzf`msB0H@bj^Iy*nOPca18 zzgm7cAWWVV)RndQ(bI;w*Uh%Fo(Q{b+qvZ0rq{M?YLf6u^HkB^ctHKg@e4or%xJyh zvVP3hqsG1A_TZJ_l~uLL7TeXaRqyXU<2Ig|G`8c_`q*RImN1E}QVx zjw5CXFYX-?#aXXoTiy&kRpD7*w{fx0OPSLe0+ovvR2-f1!nT@yp=%4yugZ_zq*BEn z7@cdknOzl!6Mt@M9x>tSNqOWmiMvB$B)lZAkX!M>kixwm&D_(SaiQX3^W)>+uDGRi zQE**pO|o^&yq^oo9^@K0u65y$>)Ixi_>T0MHOFwk8pKXoUeatzTzemX&oBuu<|h%w z{Jh%}dN$o_8`Ap2ZA+UchopxXs+N^4?~Izdr=WWOrKr`T0&1x-uAN7grA}Y-LHv=k zZ?y$JZR_zp@%s1sWHFyEll+cWSyq8?rWgd(kT@eXXH-Kc9}t zt?uQk9}m8}rX_qzT}#u3%g3WbuLi#DH!r^96Z)N<#cQ^^JS5>IUqa+o{4&~q<%I6T zZ|&OJSTK#6d-SJj!}_%^A3E4_C_1sT#K06^o>Y^t=({OXUrkzHUgvGEddkz~*cU^W zjP~&Hl3g5kS;9-srNCQUvWm4U8q#%<%ag53n|6PME~8s-J9wr&;kb%lU{Kdp zbUoaAHks0Nt=#wZbLV1aecZUS+O@mkf!3nA65eahwc9K%(Y>+HzTx>hvp;J;@R-Mn z*gc(x!fUjXb7nUB_@%{9&0Lj#M{r|X(8gDun0ES=ivf!kFWvm`>ODW5xdchn1-$Vi}QuKk~9vp5f_59G2WonszS zagYi@F4NLFS_f>T<{&kKTowW>D5N?e)dYC0jTD}cdWF;%Fi#lF18n`3i&$yMW%KT) zwcrGGUX0b4n*<8#+T`JwtXzhrW9RS;Ye_LD@T|#_5v|k?gi}8dPKxJ@0?GSVd^WP> zOMI~4!!fVZ_NWej+q*o+F)Z*UeyS{*oIbHP6Cwm#RybcDUMrh(Q{Qya2M@8rgIt5X z)BfJp3G)C|nkj=d2~`?rWKG$83>Ivh8p=zf)of)|Mymy^_}M(4&E2C`vrGn9vw5^7 zJL_qVwz3)OYwXmjYx}A&8aIT`$ak1oQnHxqa-mT_5q4C#+X9O-EEb~(L#+~W3g>l# z7+5R7u%LG24x+FpM8XNY-$q0W_7V<}-eOH-#3sw67@dVuY6hf&xk?)eA=HLlXDwpt z;`$v$w-EX3p(HK>5 100) throw 'l may not be over 100' + return true + } + + get c() { return this.#c } + set c(value: number) { + if (this._isCValid(value)) this.#c = value + } + _isCValid(value: number): boolean { + if (value < 0) throw 'c must be above 0' + if (value > 32) throw 'c may not be over 32' + return true + } + + get h() { return this.#h } + set h(value: number) { + if (this._isHValid(value)) this.#h = value + } + _isHValid(value: number): boolean { + if (value < 0) throw 'h must be above 0' + if (value > 360) throw 'h may not be over 360' + return true + } + + asOklch(): OklchInterface { + return { + l: this.#l, + c: this.#c, + h: this.#h + } + } + + toHex(): string { + return `#${convert.oklch.hex(this.#l, this.#c, this.#h)}` + } +} diff --git a/src/palette.toml b/src/palette.toml new file mode 100644 index 0000000..37963db --- /dev/null +++ b/src/palette.toml @@ -0,0 +1,13 @@ +name = "verdigris" +version = "0.0.1" + +[author] +name = "Gaiety" +email = "ava+verdigris@gaiety.me" + +[colors] + +[colors.bg] +l = 0.2 +c = 0.02 +h = diff --git a/src/palette.ts b/src/palette.ts new file mode 100644 index 0000000..7b6536a --- /dev/null +++ b/src/palette.ts @@ -0,0 +1,32 @@ +import Color from './helpers/color' + +const bg = new Color('Background', { + l: 0.2, + c: 0.02, + h: 300, +}) + +const fg = new Color('Foreground', { + l: 0.3, + c: 0.02, + h: 300, +}) + +const darkRed = new Color('Dark Red', { + l: 0.8, + c: 0.1, + h: 0, +}) + +const lightRed = new Color('Light Red', { + l: 0.9, + c: 0.1, + h: 0, +}) + +export default [ + bg, + fg, + darkRed, + lightRed +] diff --git a/tests/helpers/color.test.ts b/tests/helpers/color.test.ts new file mode 100644 index 0000000..7d7d952 --- /dev/null +++ b/tests/helpers/color.test.ts @@ -0,0 +1,86 @@ +import { expect, test } from 'bun:test' +import Color from '../../src/helpers/color' + +test('throws if given an empty name', () => { + expect( + () => new Color('', { l: 0, c: 0, h: 0 })) + .toThrow( + new Error('name may not be empty') + ) +}) + +test('can get name', () => { + const color = new Color('foo', { l: 0, c: 0, h: 0 }) + expect(color.name).toBe('foo') +}) + +test('can get Oklch values', () => { + const color = new Color('foo', { l: 1, c: 2, h: 3 }) + expect(color.l).toBe(1) + expect(color.c).toBe(2) + expect(color.h).toBe(3) +}) + +test('throws if l under range', () => { + expect( + () => new Color('foo', { l: -1, c: 0, h: 0 })) + .toThrow( + new Error('l must be above 0') + ) +}) + +test('throws if l above range', () => { + expect( + () => new Color('foo', { l: 101, c: 0, h: 0 })) + .toThrow( + new Error('l may not be over 100') + ) +}) + +test('throws if c under range', () => { + expect( + () => new Color('foo', { l: 0, c: -1, h: 0 })) + .toThrow( + new Error('c must be above 0') + ) +}) + +test('throws if c above range', () => { + expect( + () => new Color('foo', { l: 0, c: 33, h: 0 })) + .toThrow( + new Error('c may not be over 32') + ) +}) + +test('throws if h under range', () => { + expect( + () => new Color('foo', { l: 0, c: 0, h: -1 })) + .toThrow( + new Error('h must be above 0') + ) +}) + +test('throws if h above range', () => { + expect( + () => new Color('foo', { l: 0, c: 0, h: 361 })) + .toThrow( + new Error('h may not be over 360') + ) +}) + +test('can get Oklch object with values', () => { + const color = new Color('foo', { l: 0, c: 0, h: 0 }) + expect(color.asOklch()).toEqual({ l: 0, c: 0, h: 0 }) +}) + +test('can convert to expected hex values', () => { + const black = new Color('foo', { l: 0, c: 0, h: 0 }) + expect(black.toHex()).toBe('#000000') + + const yellow = new Color('foo', { l: 100, c: 32, h: 110 }) + expect(yellow.toHex()).toBe('#FFFF00') + + const white = new Color('foo', { l: 100, c: 0, h: 0 }) + expect(white.toHex()).toBe('#FFFFFF') +})