diff --git a/pkgs/flake-module.nix b/pkgs/flake-module.nix
index 52ff2084d..fb8b1536c 100644
--- a/pkgs/flake-module.nix
+++ b/pkgs/flake-module.nix
@@ -3,6 +3,7 @@
./clan-cli/flake-module.nix
./installer/flake-module.nix
./ui/flake-module.nix
+ ./theme/flake-module.nix
];
perSystem = { pkgs, config, ... }: {
diff --git a/pkgs/theme/.editorconfig b/pkgs/theme/.editorconfig
new file mode 100644
index 000000000..4e89cb0e9
--- /dev/null
+++ b/pkgs/theme/.editorconfig
@@ -0,0 +1,3 @@
+[*.{js,jsx,ts,tsx,json}]
+indent_style = space
+indent_size = 4
\ No newline at end of file
diff --git a/pkgs/theme/.envrc b/pkgs/theme/.envrc
new file mode 100644
index 000000000..2933b5bd2
--- /dev/null
+++ b/pkgs/theme/.envrc
@@ -0,0 +1,12 @@
+# Because we depend on nixpkgs sources, uploading to builders takes a long time
+
+source_up
+
+files=(flake-module.nix package.json package-lock.json)
+if type nix_direnv_watch_file &>/dev/null; then
+ nix_direnv_watch_file "${files[@]}"
+else
+ watch_file "${files[@]}"
+fi
+
+use flake .#theme --builders ''
diff --git a/pkgs/theme/.gitignore b/pkgs/theme/.gitignore
new file mode 100644
index 000000000..9e29a683b
--- /dev/null
+++ b/pkgs/theme/.gitignore
@@ -0,0 +1,43 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# nix
+.floco
+src/fonts
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# local env files
+.env*.local
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
+
+# Generated api code
+openapi.json
+api/
diff --git a/pkgs/theme/default.nix b/pkgs/theme/default.nix
new file mode 100644
index 000000000..d66639194
--- /dev/null
+++ b/pkgs/theme/default.nix
@@ -0,0 +1,41 @@
+{ floco
+, system
+, pkgs
+, clanPkgs
+}:
+let
+
+ lib = floco.lib;
+
+ pjs =
+ let
+ msg = "default.nix: Expected to find `package.json' to lookup " +
+ "package name/version, but no such file exists at: " +
+ (toString ./package.json);
+ in
+ if builtins.pathExists ./package.json then lib.importJSON ./package.json
+ else throw msg;
+ ident = pjs.name;
+ inherit (pjs) version;
+
+
+ fmod = lib.evalModules {
+ modules = [
+ floco.nixosModules.floco
+ { config.floco.settings = { inherit system; basedir = ./.; }; }
+ ./nix/floco-cfg.nix
+ ];
+ specialArgs = {
+ inherit pkgs clanPkgs;
+ };
+ };
+
+ # This attrset holds a few derivations related to our package.
+ # We'll expose these below to the CLI.
+ pkg = fmod.config.floco.packages.${ident}.${version};
+
+
+in
+{
+ inherit pkg fmod;
+}
diff --git a/pkgs/theme/flake-module.nix b/pkgs/theme/flake-module.nix
new file mode 100644
index 000000000..864cea516
--- /dev/null
+++ b/pkgs/theme/flake-module.nix
@@ -0,0 +1,18 @@
+{ self, ... }:
+{
+ perSystem = { self', pkgs, ... }:
+ let
+ inherit (self.inputs) floco;
+ base = pkgs.callPackage ./default.nix { inherit floco; clanPkgs = self'.packages; };
+ in
+ {
+ packages = {
+ theme = base.pkg.global;
+ };
+ devShells.theme = pkgs.callPackage ./shell.nix {
+ inherit pkgs;
+ inherit (base) fmod pkg;
+ clanPkgs = self'.packages;
+ };
+ };
+}
diff --git a/pkgs/theme/index.html b/pkgs/theme/index.html
new file mode 100644
index 000000000..35eadad0e
--- /dev/null
+++ b/pkgs/theme/index.html
@@ -0,0 +1,962 @@
+
+
+
+
Page Title
+
+
+
+
+ neutral0
+
+
+ neutral10
+
+
+ neutral20
+
+
+ neutral30
+
+
+ neutral40
+
+
+ neutral50
+
+
+ neutral51
+
+
+ neutral60
+
+
+ neutral70
+
+
+ neutral80
+
+
+ neutral90
+
+
+ neutral98
+
+
+ neutral100
+
+
+
+
+ red0
+
+
+ red10
+
+
+ red20
+
+
+ red30
+
+
+ red40
+
+
+ red50
+
+
+ red60
+
+
+ red70
+
+
+ red80
+
+
+ red90
+
+
+ red95
+
+
+ red100
+
+
+
+
+ green0
+
+
+ green10
+
+
+ green20
+
+
+ green30
+
+
+ green40
+
+
+ green50
+
+
+ green60
+
+
+ green70
+
+
+ green72
+
+
+ green80
+
+
+ green90
+
+
+ green98
+
+
+ green100
+
+
+
+
+ yellow0
+
+
+ yellow10
+
+
+ yellow20
+
+
+ yellow30
+
+
+ yellow40
+
+
+ yellow50
+
+
+ yellow60
+
+
+ yellow70
+
+
+ yellow80
+
+
+ yellow87
+
+
+ yellow90
+
+
+ yellow98
+
+
+ yellow100
+
+
+
+
+ purple0
+
+
+ purple10
+
+
+ purple20
+
+
+ purple30
+
+
+ purple33
+
+
+ purple40
+
+
+ purple50
+
+
+ purple60
+
+
+ purple70
+
+
+ purple80
+
+
+ purple90
+
+
+ purple100
+
+
+
+
+ blue0
+
+
+ blue10
+
+
+ blue20
+
+
+ blue30
+
+
+ blue40
+
+
+ blue50
+
+
+ blue60
+
+
+ blue70
+
+
+ blue80
+
+
+ blue90
+
+
+ blue95
+
+
+ blue100
+
+
+
+
diff --git a/pkgs/theme/nix/floco-cfg.nix b/pkgs/theme/nix/floco-cfg.nix
new file mode 100644
index 000000000..e5b5d8719
--- /dev/null
+++ b/pkgs/theme/nix/floco-cfg.nix
@@ -0,0 +1,26 @@
+# ============================================================================ #
+#
+# Aggregates configs making them available to `default.nix', `flake.nix',
+# or other projects that want to consume this module/package as a dependency.
+#
+# ---------------------------------------------------------------------------- #
+{
+ _file = "theme/nix/floco-cfg.nix";
+ imports =
+ let
+ ifExist = builtins.filter builtins.pathExists [
+ ./pdefs.nix # Generated `pdefs.nix'
+ ./foverrides.nix # Explicit config
+ ];
+ in
+ ifExist
+ ++ [
+
+ ];
+}
+# ---------------------------------------------------------------------------- #
+#
+#
+#
+# ============================================================================ #
+
diff --git a/pkgs/theme/nix/foverrides.nix b/pkgs/theme/nix/foverrides.nix
new file mode 100644
index 000000000..acdafbe3b
--- /dev/null
+++ b/pkgs/theme/nix/foverrides.nix
@@ -0,0 +1,12 @@
+{ lib, config, ... }:
+let
+ pjs = lib.importJSON ../package.json;
+ ident = pjs.name;
+ inherit (pjs) version;
+in
+{
+ config.floco.packages.${ident}.${version} =
+ {
+ source = lib.libfloco.cleanLocalSource ../.;
+ };
+}
diff --git a/pkgs/theme/nix/pdefs.nix b/pkgs/theme/nix/pdefs.nix
new file mode 100644
index 000000000..d3567754a
--- /dev/null
+++ b/pkgs/theme/nix/pdefs.nix
@@ -0,0 +1,90 @@
+{
+ floco = {
+ pdefs = {
+ "@clan/colors" = {
+ "1.0.0" = {
+ depInfo = {
+ "@material/material-color-utilities" = {
+ descriptor = "^0.2.6";
+ pin = "0.2.7";
+ };
+ "@types/node" = {
+ descriptor = "^20.3.2";
+ pin = "20.8.2";
+ };
+ typescript = {
+ descriptor = "^5.1.5";
+ pin = "5.2.2";
+ };
+ };
+ fetchInfo = "path:..";
+ ident = "@clan/colors";
+ lifecycle = {
+ build = true;
+ };
+ ltype = "dir";
+ treeInfo = {
+ "node_modules/@material/material-color-utilities" = {
+ dev = true;
+ key = "@material/material-color-utilities/0.2.7";
+ };
+ "node_modules/@types/node" = {
+ dev = true;
+ key = "@types/node/20.8.2";
+ };
+ "node_modules/typescript" = {
+ dev = true;
+ key = "typescript/5.2.2";
+ };
+ };
+ version = "1.0.0";
+ };
+ };
+ "@material/material-color-utilities" = {
+ "0.2.7" = {
+ fetchInfo = {
+ narHash = "sha256-hRYXqtkoXHoB30v1hstWz7dO7dNeBb6EJqZG66hHi94=";
+ type = "tarball";
+ url = "https://registry.npmjs.org/@material/material-color-utilities/-/material-color-utilities-0.2.7.tgz";
+ };
+ ident = "@material/material-color-utilities";
+ ltype = "file";
+ treeInfo = { };
+ version = "0.2.7";
+ };
+ };
+ "@types/node" = {
+ "20.8.2" = {
+ fetchInfo = {
+ narHash = "sha256-o4hyob1kLnm0OE8Rngm0d6XJxobpMlYSoquusktmLPk=";
+ type = "tarball";
+ url = "https://registry.npmjs.org/@types/node/-/node-20.8.2.tgz";
+ };
+ ident = "@types/node";
+ ltype = "file";
+ treeInfo = { };
+ version = "20.8.2";
+ };
+ };
+ typescript = {
+ "5.2.2" = {
+ binInfo = {
+ binPairs = {
+ tsc = "bin/tsc";
+ tsserver = "bin/tsserver";
+ };
+ };
+ fetchInfo = {
+ narHash = "sha256-io9rXH9RLRLB0484ZdvcqblLQquLFUBGxDuwSixWxus=";
+ type = "tarball";
+ url = "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz";
+ };
+ ident = "typescript";
+ ltype = "file";
+ treeInfo = { };
+ version = "5.2.2";
+ };
+ };
+ };
+ };
+}
diff --git a/pkgs/theme/package-lock.json b/pkgs/theme/package-lock.json
new file mode 100644
index 000000000..6a755684b
--- /dev/null
+++ b/pkgs/theme/package-lock.json
@@ -0,0 +1,63 @@
+{
+ "name": "@clan/colors",
+ "version": "1.0.0",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "@clan/colors",
+ "version": "1.0.0",
+ "license": "ISC",
+ "devDependencies": {
+ "@material/material-color-utilities": "^0.2.6",
+ "@types/node": "^20.3.2",
+ "typescript": "^5.1.5"
+ }
+ },
+ "node_modules/@material/material-color-utilities": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/@material/material-color-utilities/-/material-color-utilities-0.2.7.tgz",
+ "integrity": "sha512-0FCeqG6WvK4/Cc06F/xXMd/pv4FeisI0c1tUpBbfhA2n9Y8eZEv4Karjbmf2ZqQCPUWMrGp8A571tCjizxoTiQ==",
+ "dev": true
+ },
+ "node_modules/@types/node": {
+ "version": "20.8.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.2.tgz",
+ "integrity": "sha512-Vvycsc9FQdwhxE3y3DzeIxuEJbWGDsnrxvMADzTDF/lcdR9/K+AQIeAghTQsHtotg/q0j3WEOYS/jQgSdWue3w==",
+ "dev": true
+ },
+ "node_modules/typescript": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
+ "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
+ "dev": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ }
+ },
+ "dependencies": {
+ "@material/material-color-utilities": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/@material/material-color-utilities/-/material-color-utilities-0.2.7.tgz",
+ "integrity": "sha512-0FCeqG6WvK4/Cc06F/xXMd/pv4FeisI0c1tUpBbfhA2n9Y8eZEv4Karjbmf2ZqQCPUWMrGp8A571tCjizxoTiQ==",
+ "dev": true
+ },
+ "@types/node": {
+ "version": "20.8.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.2.tgz",
+ "integrity": "sha512-Vvycsc9FQdwhxE3y3DzeIxuEJbWGDsnrxvMADzTDF/lcdR9/K+AQIeAghTQsHtotg/q0j3WEOYS/jQgSdWue3w==",
+ "dev": true
+ },
+ "typescript": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
+ "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
+ "dev": true
+ }
+ }
+}
diff --git a/pkgs/theme/package.json b/pkgs/theme/package.json
new file mode 100644
index 000000000..febd95e29
--- /dev/null
+++ b/pkgs/theme/package.json
@@ -0,0 +1,23 @@
+{
+ "name": "@clan/colors",
+ "version": "1.0.0",
+ "description": "",
+ "type": "module",
+ "files": [
+ "colors.json"
+ ],
+ "scripts": {
+ "typecheck": "./node_modules/.bin/tsc -p ./tsconfig.json --noEmit",
+ "build": "tsc --build --clean && tsc && node ./build/main.js",
+ "html": "tsc --build --clean && tsc && node ./build/generate.js",
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "devDependencies": {
+ "@material/material-color-utilities": "^0.2.6",
+ "typescript": "^5.1.5",
+ "@types/node": "^20.3.2"
+ }
+}
diff --git a/pkgs/theme/shell.nix b/pkgs/theme/shell.nix
new file mode 100644
index 000000000..697ce02dc
--- /dev/null
+++ b/pkgs/theme/shell.nix
@@ -0,0 +1,24 @@
+{ fmod
+, pkg
+, pkgs
+, clanPkgs
+}:
+pkgs.mkShell {
+ buildInputs = [
+ fmod.config.floco.settings.nodePackage
+ ];
+ shellHook = ''
+ ID=${pkg.built.tree}
+ currID=$(cat .floco/.node_modules_id 2> /dev/null)
+
+ mkdir -p .floco
+ if [[ "$ID" != "$currID" || ! -d "node_modules" ]];
+ then
+ ${pkgs.rsync}/bin/rsync -a --chmod=ug+w --delete ${pkg.built.tree}/node_modules/ ./node_modules/
+ echo -n $ID > .floco/.node_modules_id
+ echo "floco ok: node_modules updated"
+ fi
+
+ export PATH="$PATH:$(realpath ./node_modules)/.bin"
+ '';
+}
diff --git a/pkgs/theme/src/colors.json b/pkgs/theme/src/colors.json
new file mode 100644
index 000000000..00b3d2da1
--- /dev/null
+++ b/pkgs/theme/src/colors.json
@@ -0,0 +1,1507 @@
+{
+ "ref": {
+ "palette": {
+ "neutral0": {
+ "type": "color",
+ "value": "#000000",
+ "meta": {
+ "color": {
+ "shade": 0,
+ "name": "neutral0",
+ "baseName": "neutral",
+ "value": {
+ "argb": 4278190080,
+ "internalHue": 0,
+ "internalChroma": 0,
+ "internalTone": 0
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "neutral10": {
+ "type": "color",
+ "value": "#1f1926",
+ "meta": {
+ "color": {
+ "shade": 10.003759056317755,
+ "name": "neutral10",
+ "baseName": "neutral",
+ "value": {
+ "argb": 4280228134,
+ "internalHue": 307.5177900911134,
+ "internalChroma": 12.31060913358082,
+ "internalTone": 10.003759056317755
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "neutral20": {
+ "type": "color",
+ "value": "#342e3c",
+ "meta": {
+ "color": {
+ "shade": 20.085095626697807,
+ "name": "neutral20",
+ "baseName": "neutral",
+ "value": {
+ "argb": 4281609788,
+ "internalHue": 305.10252972150806,
+ "internalChroma": 12.306039972203871,
+ "internalTone": 20.085095626697807
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "neutral30": {
+ "type": "color",
+ "value": "#4b4453",
+ "meta": {
+ "color": {
+ "shade": 30.046003376538273,
+ "name": "neutral30",
+ "baseName": "neutral",
+ "value": {
+ "argb": 4283122771,
+ "internalHue": 305.98377855280984,
+ "internalChroma": 12.435661561528065,
+ "internalTone": 30.046003376538273
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "neutral40": {
+ "type": "color",
+ "value": "#635b6b",
+ "meta": {
+ "color": {
+ "shade": 39.90923680162607,
+ "name": "neutral40",
+ "baseName": "neutral",
+ "value": {
+ "argb": 4284701547,
+ "internalHue": 306.66704710484186,
+ "internalChroma": 12.60180604336879,
+ "internalTone": 39.90923680162607
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "neutral50": {
+ "type": "color",
+ "value": "#7d7485",
+ "meta": {
+ "color": {
+ "shade": 50.149634074630896,
+ "name": "neutral50",
+ "baseName": "neutral",
+ "value": {
+ "argb": 4286411909,
+ "internalHue": 307.1595078617906,
+ "internalChroma": 12.771029812055264,
+ "internalTone": 50.149634074630896
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "neutral60": {
+ "type": "color",
+ "value": "#978d9f",
+ "meta": {
+ "color": {
+ "shade": 60.00804427695178,
+ "name": "neutral60",
+ "baseName": "neutral",
+ "value": {
+ "argb": 4288122271,
+ "internalHue": 307.58417411006155,
+ "internalChroma": 12.975589878795695,
+ "internalTone": 60.00804427695178
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "neutral70": {
+ "type": "color",
+ "value": "#b2a7ba",
+ "meta": {
+ "color": {
+ "shade": 69.92830090754354,
+ "name": "neutral70",
+ "baseName": "neutral",
+ "value": {
+ "argb": 4289898426,
+ "internalHue": 307.9163792598157,
+ "internalChroma": 13.186314353672046,
+ "internalTone": 69.92830090754354
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "neutral80": {
+ "type": "color",
+ "value": "#cec2d5",
+ "meta": {
+ "color": {
+ "shade": 79.90290481197965,
+ "name": "neutral80",
+ "baseName": "neutral",
+ "value": {
+ "argb": 4291740373,
+ "internalHue": 309.11365640920496,
+ "internalChroma": 12.860746572272111,
+ "internalTone": 79.90290481197965
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "neutral90": {
+ "type": "color",
+ "value": "#eadef2",
+ "meta": {
+ "color": {
+ "shade": 89.92698389845121,
+ "name": "neutral90",
+ "baseName": "neutral",
+ "value": {
+ "argb": 4293582578,
+ "internalHue": 307.1244282338364,
+ "internalChroma": 13.016607436721062,
+ "internalTone": 89.92698389845121
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "neutral98": {
+ "type": "color",
+ "value": "#fff7ff",
+ "meta": {
+ "color": {
+ "shade": 98.03413002191978,
+ "name": "neutral98",
+ "baseName": "neutral",
+ "value": {
+ "argb": 4294965247,
+ "internalHue": 308.5710968231098,
+ "internalChroma": 5.957979276536917,
+ "internalTone": 98.03413002191978
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "neutral100": {
+ "type": "color",
+ "value": "#ffffff",
+ "meta": {
+ "color": {
+ "shade": 100,
+ "name": "neutral100",
+ "baseName": "neutral",
+ "value": {
+ "argb": 4294967295,
+ "internalHue": 209.49195947383808,
+ "internalChroma": 2.8690352036774005,
+ "internalTone": 100
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "neutral51": {
+ "type": "color",
+ "value": "#807788",
+ "meta": {
+ "color": {
+ "shade": 51.33652653847143,
+ "name": "neutral51",
+ "baseName": "neutral",
+ "value": {
+ "argb": 4286609288,
+ "internalHue": 307.02674903511826,
+ "internalChroma": 12.712042625104525,
+ "internalTone": 51.33652653847143
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "red0": {
+ "type": "color",
+ "value": "#000000",
+ "meta": {
+ "color": {
+ "shade": 0,
+ "name": "red0",
+ "baseName": "red",
+ "value": {
+ "argb": 4278190080,
+ "internalHue": 0,
+ "internalChroma": 0,
+ "internalTone": 0
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "red10": {
+ "type": "color",
+ "value": "#410006",
+ "meta": {
+ "color": {
+ "shade": 10.083978498281937,
+ "name": "red10",
+ "baseName": "red",
+ "value": {
+ "argb": 4282449926,
+ "internalHue": 20.49137889592658,
+ "internalChroma": 46.65679825348804,
+ "internalTone": 10.083978498281937
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "red20": {
+ "type": "color",
+ "value": "#680010",
+ "meta": {
+ "color": {
+ "shade": 19.965498139437706,
+ "name": "red20",
+ "baseName": "red",
+ "value": {
+ "argb": 4285005840,
+ "internalHue": 20.03561855894553,
+ "internalChroma": 61.86972408109188,
+ "internalTone": 19.965498139437706
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "red30": {
+ "type": "color",
+ "value": "#93001a",
+ "meta": {
+ "color": {
+ "shade": 30.102356005398256,
+ "name": "red30",
+ "baseName": "red",
+ "value": {
+ "argb": 4287823898,
+ "internalHue": 20.37329952477052,
+ "internalChroma": 76.79452989230481,
+ "internalTone": 30.102356005398256
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "red40": {
+ "type": "color",
+ "value": "#bf0026",
+ "meta": {
+ "color": {
+ "shade": 39.942423818085885,
+ "name": "red40",
+ "baseName": "red",
+ "value": {
+ "argb": 4290707494,
+ "internalHue": 20.106199387791385,
+ "internalChroma": 90.32111969449794,
+ "internalTone": 39.942423818085885
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "red50": {
+ "type": "color",
+ "value": "#e82439",
+ "meta": {
+ "color": {
+ "shade": 50.34968418397237,
+ "name": "red50",
+ "baseName": "red",
+ "value": {
+ "argb": 4293403705,
+ "internalHue": 20.251658998290857,
+ "internalChroma": 94.07638421868364,
+ "internalTone": 50.34968418397237
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "red60": {
+ "type": "color",
+ "value": "#ff5358",
+ "meta": {
+ "color": {
+ "shade": 60.0249972817316,
+ "name": "red60",
+ "baseName": "red",
+ "value": {
+ "argb": 4294923096,
+ "internalHue": 20.383817287020523,
+ "internalChroma": 80.36698599157805,
+ "internalTone": 60.0249972817316
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "red70": {
+ "type": "color",
+ "value": "#ff8986",
+ "meta": {
+ "color": {
+ "shade": 70.08653691395159,
+ "name": "red70",
+ "baseName": "red",
+ "value": {
+ "argb": 4294936966,
+ "internalHue": 20.545762347155392,
+ "internalChroma": 50.64062212391832,
+ "internalTone": 70.08653691395159
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "red80": {
+ "type": "color",
+ "value": "#ffb3b0",
+ "meta": {
+ "color": {
+ "shade": 79.97373946776993,
+ "name": "red80",
+ "baseName": "red",
+ "value": {
+ "argb": 4294947760,
+ "internalHue": 20.18275539730477,
+ "internalChroma": 29.920657113607206,
+ "internalTone": 79.97373946776993
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "red90": {
+ "type": "color",
+ "value": "#ffdad8",
+ "meta": {
+ "color": {
+ "shade": 90.02653678231813,
+ "name": "red90",
+ "baseName": "red",
+ "value": {
+ "argb": 4294957784,
+ "internalHue": 20.047520036047214,
+ "internalChroma": 13.328211730043297,
+ "internalTone": 90.02653678231813
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "red95": {
+ "type": "color",
+ "value": "#ffedeb",
+ "meta": {
+ "color": {
+ "shade": 95.08785149010296,
+ "name": "red95",
+ "baseName": "red",
+ "value": {
+ "argb": 4294962667,
+ "internalHue": 23.107458413416488,
+ "internalChroma": 5.762203689575029,
+ "internalTone": 95.08785149010296
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "red100": {
+ "type": "color",
+ "value": "#ffffff",
+ "meta": {
+ "color": {
+ "shade": 100,
+ "name": "red100",
+ "baseName": "red",
+ "value": {
+ "argb": 4294967295,
+ "internalHue": 209.49195947383808,
+ "internalChroma": 2.8690352036774005,
+ "internalTone": 100
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "green0": {
+ "type": "color",
+ "value": "#000000",
+ "meta": {
+ "color": {
+ "shade": 0,
+ "name": "green0",
+ "baseName": "green",
+ "value": {
+ "argb": 4278190080,
+ "internalHue": 0,
+ "internalChroma": 0,
+ "internalTone": 0
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "green10": {
+ "type": "color",
+ "value": "#0f2000",
+ "meta": {
+ "color": {
+ "shade": 10.065731493614088,
+ "name": "green10",
+ "baseName": "green",
+ "value": {
+ "argb": 4279181312,
+ "internalHue": 132.6377508621778,
+ "internalChroma": 27.898808872542403,
+ "internalTone": 10.065731493614088
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "green20": {
+ "type": "color",
+ "value": "#1d3700",
+ "meta": {
+ "color": {
+ "shade": 20.0181970675321,
+ "name": "green20",
+ "baseName": "green",
+ "value": {
+ "argb": 4280104704,
+ "internalHue": 132.90351159718293,
+ "internalChroma": 37.40556508798026,
+ "internalTone": 20.0181970675321
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "green30": {
+ "type": "color",
+ "value": "#2d5000",
+ "meta": {
+ "color": {
+ "shade": 30.14536245858975,
+ "name": "green30",
+ "baseName": "green",
+ "value": {
+ "argb": 4281159680,
+ "internalHue": 132.7178262928362,
+ "internalChroma": 46.263150659055384,
+ "internalTone": 30.14536245858975
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "green40": {
+ "type": "color",
+ "value": "#3d6a00",
+ "meta": {
+ "color": {
+ "shade": 40.081500150264795,
+ "name": "green40",
+ "baseName": "green",
+ "value": {
+ "argb": 4282214912,
+ "internalHue": 132.8037028669833,
+ "internalChroma": 54.73225096061547,
+ "internalTone": 40.081500150264795
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "green50": {
+ "type": "color",
+ "value": "#4e8500",
+ "meta": {
+ "color": {
+ "shade": 49.97144885704813,
+ "name": "green50",
+ "baseName": "green",
+ "value": {
+ "argb": 4283335936,
+ "internalHue": 132.75436059574238,
+ "internalChroma": 62.72422364559764,
+ "internalTone": 49.97144885704813
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "green60": {
+ "type": "color",
+ "value": "#60a100",
+ "meta": {
+ "color": {
+ "shade": 59.86753304815464,
+ "name": "green60",
+ "baseName": "green",
+ "value": {
+ "argb": 4284522752,
+ "internalHue": 132.63241512726003,
+ "internalChroma": 70.35443213470766,
+ "internalTone": 59.86753304815464
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "green70": {
+ "type": "color",
+ "value": "#74be11",
+ "meta": {
+ "color": {
+ "shade": 69.8809251786242,
+ "name": "green70",
+ "baseName": "green",
+ "value": {
+ "argb": 4285840913,
+ "internalHue": 132.68991625104582,
+ "internalChroma": 75.97156752943617,
+ "internalTone": 69.8809251786242
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "green80": {
+ "type": "color",
+ "value": "#8edb34",
+ "meta": {
+ "color": {
+ "shade": 79.98896805851334,
+ "name": "green80",
+ "baseName": "green",
+ "value": {
+ "argb": 4287552308,
+ "internalHue": 132.69953995344068,
+ "internalChroma": 76.19831467398797,
+ "internalTone": 79.98896805851334
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "green90": {
+ "type": "color",
+ "value": "#a9f850",
+ "meta": {
+ "color": {
+ "shade": 89.92847905213094,
+ "name": "green90",
+ "baseName": "green",
+ "value": {
+ "argb": 4289329232,
+ "internalHue": 132.6291026678884,
+ "internalChroma": 75.92801750984052,
+ "internalTone": 89.92847905213094
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "green98": {
+ "type": "color",
+ "value": "#efffd8",
+ "meta": {
+ "color": {
+ "shade": 97.9648930849126,
+ "name": "green98",
+ "baseName": "green",
+ "value": {
+ "argb": 4293918680,
+ "internalHue": 133.15723708282297,
+ "internalChroma": 19.52307057814198,
+ "internalTone": 97.9648930849126
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "green100": {
+ "type": "color",
+ "value": "#ffffff",
+ "meta": {
+ "color": {
+ "shade": 100,
+ "name": "green100",
+ "baseName": "green",
+ "value": {
+ "argb": 4294967295,
+ "internalHue": 209.49195947383808,
+ "internalChroma": 2.8690352036774005,
+ "internalTone": 100
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "green72": {
+ "type": "color",
+ "value": "#7ac51b",
+ "meta": {
+ "color": {
+ "shade": 72.32811726939526,
+ "name": "green72",
+ "baseName": "green",
+ "value": {
+ "argb": 4286235931,
+ "internalHue": 132.72088598890844,
+ "internalChroma": 76.1645765194525,
+ "internalTone": 72.32811726939526
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "yellow0": {
+ "type": "color",
+ "value": "#000000",
+ "meta": {
+ "color": {
+ "shade": 0,
+ "name": "yellow0",
+ "baseName": "yellow",
+ "value": {
+ "argb": 4278190080,
+ "internalHue": 0,
+ "internalChroma": 0,
+ "internalTone": 0
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "yellow10": {
+ "type": "color",
+ "value": "#1d1d00",
+ "meta": {
+ "color": {
+ "shade": 10.106703477257245,
+ "name": "yellow10",
+ "baseName": "yellow",
+ "value": {
+ "argb": 4280098048,
+ "internalHue": 111.03877590605597,
+ "internalChroma": 21.59454563470911,
+ "internalTone": 10.106703477257245
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "yellow20": {
+ "type": "color",
+ "value": "#323200",
+ "meta": {
+ "color": {
+ "shade": 19.880212207873114,
+ "name": "yellow20",
+ "baseName": "yellow",
+ "value": {
+ "argb": 4281479680,
+ "internalHue": 111.0399366950529,
+ "internalChroma": 28.690976813757185,
+ "internalTone": 19.880212207873114
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "yellow30": {
+ "type": "color",
+ "value": "#494900",
+ "meta": {
+ "color": {
+ "shade": 29.865967480952882,
+ "name": "yellow30",
+ "baseName": "yellow",
+ "value": {
+ "argb": 4282992896,
+ "internalHue": 111.04120661143587,
+ "internalChroma": 35.52733088146877,
+ "internalTone": 29.865967480952882
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "yellow40": {
+ "type": "color",
+ "value": "#626200",
+ "meta": {
+ "color": {
+ "shade": 40.134168675400446,
+ "name": "yellow40",
+ "baseName": "yellow",
+ "value": {
+ "argb": 4284637696,
+ "internalHue": 111.04258415006367,
+ "internalChroma": 42.22832005957653,
+ "internalTone": 40.134168675400446
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "yellow50": {
+ "type": "color",
+ "value": "#7b7b00",
+ "meta": {
+ "color": {
+ "shade": 49.95005800072835,
+ "name": "yellow50",
+ "baseName": "yellow",
+ "value": {
+ "argb": 4286282496,
+ "internalHue": 111.04395807340283,
+ "internalChroma": 48.38382892913055,
+ "internalTone": 49.95005800072835
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "yellow60": {
+ "type": "color",
+ "value": "#969600",
+ "meta": {
+ "color": {
+ "shade": 60.15586763883118,
+ "name": "yellow60",
+ "baseName": "yellow",
+ "value": {
+ "argb": 4288058880,
+ "internalHue": 111.04543734311906,
+ "internalChroma": 54.56632971724491,
+ "internalTone": 60.15586763883118
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "yellow70": {
+ "type": "color",
+ "value": "#b1b100",
+ "meta": {
+ "color": {
+ "shade": 70.02952002930259,
+ "name": "yellow70",
+ "baseName": "yellow",
+ "value": {
+ "argb": 4289835264,
+ "internalHue": 111.0469115145827,
+ "internalChroma": 60.36469563357617,
+ "internalTone": 70.02952002930259
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "yellow80": {
+ "type": "color",
+ "value": "#cdcd00",
+ "meta": {
+ "color": {
+ "shade": 79.97768808984283,
+ "name": "yellow80",
+ "baseName": "yellow",
+ "value": {
+ "argb": 4291677440,
+ "internalHue": 111.04843463241409,
+ "internalChroma": 66.04564867131279,
+ "internalTone": 79.97768808984283
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "yellow90": {
+ "type": "color",
+ "value": "#eaea2c",
+ "meta": {
+ "color": {
+ "shade": 90.10018523056145,
+ "name": "yellow90",
+ "baseName": "yellow",
+ "value": {
+ "argb": 4293585452,
+ "internalHue": 111.05850991954583,
+ "internalChroma": 67.11150141490174,
+ "internalTone": 90.10018523056145
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "yellow98": {
+ "type": "color",
+ "value": "#fffeac",
+ "meta": {
+ "color": {
+ "shade": 98.08238369776234,
+ "name": "yellow98",
+ "baseName": "yellow",
+ "value": {
+ "argb": 4294966956,
+ "internalHue": 111.25888789536712,
+ "internalChroma": 32.85492880156371,
+ "internalTone": 98.08238369776234
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "yellow100": {
+ "type": "color",
+ "value": "#ffffff",
+ "meta": {
+ "color": {
+ "shade": 100,
+ "name": "yellow100",
+ "baseName": "yellow",
+ "value": {
+ "argb": 4294967295,
+ "internalHue": 209.49195947383808,
+ "internalChroma": 2.8690352036774005,
+ "internalTone": 100
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "yellow87": {
+ "type": "color",
+ "value": "#e0e01f",
+ "meta": {
+ "color": {
+ "shade": 86.63141709367274,
+ "name": "yellow87",
+ "baseName": "yellow",
+ "value": {
+ "argb": 4292927519,
+ "internalHue": 111.05372283636466,
+ "internalChroma": 67.03912845344203,
+ "internalTone": 86.63141709367274
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "purple0": {
+ "type": "color",
+ "value": "#000000",
+ "meta": {
+ "color": {
+ "shade": 0,
+ "name": "purple0",
+ "baseName": "purple",
+ "value": {
+ "argb": 4278190080,
+ "internalHue": 0,
+ "internalChroma": 0,
+ "internalTone": 0
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "purple10": {
+ "type": "color",
+ "value": "#270057",
+ "meta": {
+ "color": {
+ "shade": 9.949371183187875,
+ "name": "purple10",
+ "baseName": "purple",
+ "value": {
+ "argb": 4280746071,
+ "internalHue": 304.2328522111427,
+ "internalChroma": 46.437333166555376,
+ "internalTone": 9.949371183187875
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "purple20": {
+ "type": "color",
+ "value": "#42008a",
+ "meta": {
+ "color": {
+ "shade": 20.016758807912623,
+ "name": "purple20",
+ "baseName": "purple",
+ "value": {
+ "argb": 4282515594,
+ "internalHue": 304.36784556077544,
+ "internalChroma": 61.50458145218004,
+ "internalTone": 20.016758807912623
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "purple30": {
+ "type": "color",
+ "value": "#5e07bd",
+ "meta": {
+ "color": {
+ "shade": 29.925766650608445,
+ "name": "purple30",
+ "baseName": "purple",
+ "value": {
+ "argb": 4284352445,
+ "internalHue": 304.4277771908796,
+ "internalChroma": 74.23877180929804,
+ "internalTone": 29.925766650608445
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "purple40": {
+ "type": "color",
+ "value": "#7734d6",
+ "meta": {
+ "color": {
+ "shade": 39.97009865160796,
+ "name": "purple40",
+ "baseName": "purple",
+ "value": {
+ "argb": 4286002390,
+ "internalHue": 304.45603091543944,
+ "internalChroma": 74.3412000696465,
+ "internalTone": 39.97009865160796
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "purple50": {
+ "type": "color",
+ "value": "#9152f1",
+ "meta": {
+ "color": {
+ "shade": 49.98393171826943,
+ "name": "purple50",
+ "baseName": "purple",
+ "value": {
+ "argb": 4287714033,
+ "internalHue": 304.4729041253868,
+ "internalChroma": 74.3979039466535,
+ "internalTone": 49.98393171826943
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "purple60": {
+ "type": "color",
+ "value": "#a974ff",
+ "meta": {
+ "color": {
+ "shade": 60.020230255269766,
+ "name": "purple60",
+ "baseName": "purple",
+ "value": {
+ "argb": 4289295615,
+ "internalHue": 304.33346937174144,
+ "internalChroma": 67.696770270822,
+ "internalTone": 60.020230255269766
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "purple70": {
+ "type": "color",
+ "value": "#bf98ff",
+ "meta": {
+ "color": {
+ "shade": 70.00240792964836,
+ "name": "purple70",
+ "baseName": "purple",
+ "value": {
+ "argb": 4290746623,
+ "internalHue": 304.35303537072394,
+ "internalChroma": 53.377783125592956,
+ "internalTone": 70.00240792964836
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "purple80": {
+ "type": "color",
+ "value": "#d5baff",
+ "meta": {
+ "color": {
+ "shade": 79.88845697732896,
+ "name": "purple80",
+ "baseName": "purple",
+ "value": {
+ "argb": 4292197119,
+ "internalHue": 304.33781122020633,
+ "internalChroma": 37.882491293353276,
+ "internalTone": 79.88845697732896
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "purple90": {
+ "type": "color",
+ "value": "#ecdcff",
+ "meta": {
+ "color": {
+ "shade": 89.97031492533036,
+ "name": "purple90",
+ "baseName": "purple",
+ "value": {
+ "argb": 4293713151,
+ "internalHue": 304.65634204424674,
+ "internalChroma": 20.875946834052293,
+ "internalTone": 89.97031492533036
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "purple100": {
+ "type": "color",
+ "value": "#ffffff",
+ "meta": {
+ "color": {
+ "shade": 100,
+ "name": "purple100",
+ "baseName": "purple",
+ "value": {
+ "argb": 4294967295,
+ "internalHue": 209.49195947383808,
+ "internalChroma": 2.8690352036774005,
+ "internalTone": 100
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "purple33": {
+ "type": "color",
+ "value": "#661bc5",
+ "meta": {
+ "color": {
+ "shade": 33.22131355688631,
+ "name": "purple33",
+ "baseName": "purple",
+ "value": {
+ "argb": 4284881861,
+ "internalHue": 304.39679947806945,
+ "internalChroma": 74.29428037092354,
+ "internalTone": 33.22131355688631
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "blue0": {
+ "type": "color",
+ "value": "#000000",
+ "meta": {
+ "color": {
+ "shade": 0,
+ "name": "blue0",
+ "baseName": "blue",
+ "value": {
+ "argb": 4278190080,
+ "internalHue": 0,
+ "internalChroma": 0,
+ "internalTone": 0
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "blue10": {
+ "type": "color",
+ "value": "#001d36",
+ "meta": {
+ "color": {
+ "shade": 10.1458152722198,
+ "name": "blue10",
+ "baseName": "blue",
+ "value": {
+ "argb": 4278197558,
+ "internalHue": 253.09732762984137,
+ "internalChroma": 24.834944519214517,
+ "internalTone": 10.1458152722198
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "blue20": {
+ "type": "color",
+ "value": "#003258",
+ "meta": {
+ "color": {
+ "shade": 19.986877307700773,
+ "name": "blue20",
+ "baseName": "blue",
+ "value": {
+ "argb": 4278202968,
+ "internalHue": 253.66023249421312,
+ "internalChroma": 33.065632633907285,
+ "internalTone": 19.986877307700773
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "blue30": {
+ "type": "color",
+ "value": "#00497c",
+ "meta": {
+ "color": {
+ "shade": 29.961653191648097,
+ "name": "blue30",
+ "baseName": "blue",
+ "value": {
+ "argb": 4278208892,
+ "internalHue": 253.25872418743495,
+ "internalChroma": 40.50741919101272,
+ "internalTone": 29.961653191648097
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "blue40": {
+ "type": "color",
+ "value": "#0061a3",
+ "meta": {
+ "color": {
+ "shade": 39.90486789926618,
+ "name": "blue40",
+ "baseName": "blue",
+ "value": {
+ "argb": 4278215075,
+ "internalHue": 253.61785825827238,
+ "internalChroma": 48.00348281517394,
+ "internalTone": 39.90486789926618
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "blue50": {
+ "type": "color",
+ "value": "#1b7ac5",
+ "meta": {
+ "color": {
+ "shade": 49.71767580376719,
+ "name": "blue50",
+ "baseName": "blue",
+ "value": {
+ "argb": 4279990981,
+ "internalHue": 253.18446649407957,
+ "internalChroma": 52.16079963673709,
+ "internalTone": 49.71767580376719
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "blue60": {
+ "type": "color",
+ "value": "#4395e2",
+ "meta": {
+ "color": {
+ "shade": 60.05026394685983,
+ "name": "blue60",
+ "baseName": "blue",
+ "value": {
+ "argb": 4282619362,
+ "internalHue": 253.19633607389105,
+ "internalChroma": 52.28109638273356,
+ "internalTone": 60.05026394685983
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "blue70": {
+ "type": "color",
+ "value": "#62b0fe",
+ "meta": {
+ "color": {
+ "shade": 70.03774643292513,
+ "name": "blue70",
+ "baseName": "blue",
+ "value": {
+ "argb": 4284657918,
+ "internalHue": 252.836495127207,
+ "internalChroma": 52.02951054971474,
+ "internalTone": 70.03774643292513
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "blue80": {
+ "type": "color",
+ "value": "#9ecaff",
+ "meta": {
+ "color": {
+ "shade": 80.02758180930664,
+ "name": "blue80",
+ "baseName": "blue",
+ "value": {
+ "argb": 4288596735,
+ "internalHue": 253.4392269209713,
+ "internalChroma": 37.18607759380727,
+ "internalTone": 80.02758180930664
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "blue90": {
+ "type": "color",
+ "value": "#d1e4ff",
+ "meta": {
+ "color": {
+ "shade": 89.98090008988925,
+ "name": "blue90",
+ "baseName": "blue",
+ "value": {
+ "argb": 4291945727,
+ "internalHue": 253.51997372711435,
+ "internalChroma": 20.64342844985456,
+ "internalTone": 89.98090008988925
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "blue95": {
+ "type": "color",
+ "value": "#e9f1ff",
+ "meta": {
+ "color": {
+ "shade": 94.93066059667177,
+ "name": "blue95",
+ "baseName": "blue",
+ "value": {
+ "argb": 4293521919,
+ "internalHue": 253.05366698660546,
+ "internalChroma": 11.77862409499581,
+ "internalTone": 94.93066059667177
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "blue100": {
+ "type": "color",
+ "value": "#ffffff",
+ "meta": {
+ "color": {
+ "shade": 100,
+ "name": "blue100",
+ "baseName": "blue",
+ "value": {
+ "argb": 4294967295,
+ "internalHue": 209.49195947383808,
+ "internalChroma": 2.8690352036774005,
+ "internalTone": 100
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ }
+ },
+ "alias": {
+ "primary0": {
+ "value": "{ref.palette.purple0}"
+ },
+ "primary10": {
+ "value": "{ref.palette.purple10}"
+ },
+ "primary20": {
+ "value": "{ref.palette.purple20}"
+ },
+ "primary30": {
+ "value": "{ref.palette.purple30}"
+ },
+ "primary33": {
+ "value": "{ref.palette.purple33}"
+ },
+ "primary40": {
+ "value": "{ref.palette.purple40}"
+ },
+ "primary50": {
+ "value": "{ref.palette.purple50}"
+ },
+ "primary60": {
+ "value": "{ref.palette.purple60}"
+ },
+ "primary70": {
+ "value": "{ref.palette.purple70}"
+ },
+ "primary80": {
+ "value": "{ref.palette.purple80}"
+ },
+ "primary90": {
+ "value": "{ref.palette.purple90}"
+ },
+ "primary100": {
+ "value": "{ref.palette.purple100}"
+ },
+ "secondary0": {
+ "value": "{ref.palette.green0}"
+ },
+ "secondary10": {
+ "value": "{ref.palette.green10}"
+ },
+ "secondary20": {
+ "value": "{ref.palette.green20}"
+ },
+ "secondary30": {
+ "value": "{ref.palette.green30}"
+ },
+ "secondary40": {
+ "value": "{ref.palette.green40}"
+ },
+ "secondary50": {
+ "value": "{ref.palette.green50}"
+ },
+ "secondary60": {
+ "value": "{ref.palette.green60}"
+ },
+ "secondary70": {
+ "value": "{ref.palette.green70}"
+ },
+ "secondary72": {
+ "value": "{ref.palette.green72}"
+ },
+ "secondary80": {
+ "value": "{ref.palette.green80}"
+ },
+ "secondary90": {
+ "value": "{ref.palette.green90}"
+ },
+ "secondary98": {
+ "value": "{ref.palette.green98}"
+ },
+ "secondary100": {
+ "value": "{ref.palette.green100}"
+ },
+ "error0": {
+ "value": "{ref.palette.red0}"
+ },
+ "error10": {
+ "value": "{ref.palette.red10}"
+ },
+ "error20": {
+ "value": "{ref.palette.red20}"
+ },
+ "error30": {
+ "value": "{ref.palette.red30}"
+ },
+ "error40": {
+ "value": "{ref.palette.red40}"
+ },
+ "error50": {
+ "value": "{ref.palette.red50}"
+ },
+ "error60": {
+ "value": "{ref.palette.red60}"
+ },
+ "error70": {
+ "value": "{ref.palette.red70}"
+ },
+ "error80": {
+ "value": "{ref.palette.red80}"
+ },
+ "error90": {
+ "value": "{ref.palette.red90}"
+ },
+ "error95": {
+ "value": "{ref.palette.red95}"
+ },
+ "error100": {
+ "value": "{ref.palette.red100}"
+ }
+ },
+ "common": {
+ "white": {
+ "type": "color",
+ "value": "#ffffff",
+ "meta": {
+ "color": {
+ "shade": 100,
+ "name": "white100",
+ "baseName": "white",
+ "value": {
+ "argb": 4294967295,
+ "internalHue": 209.49195947383808,
+ "internalChroma": 2.8690352036774005,
+ "internalTone": 100
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ },
+ "black": {
+ "type": "color",
+ "value": "#000000",
+ "meta": {
+ "color": {
+ "shade": 0,
+ "name": "black0",
+ "baseName": "black",
+ "value": {
+ "argb": 4278190080,
+ "internalHue": 0,
+ "internalChroma": 0,
+ "internalTone": 0
+ }
+ },
+ "date": "2023-10-03T13:01:35.376Z"
+ }
+ }
+ }
+ }
+}
diff --git a/pkgs/theme/src/config.ts b/pkgs/theme/src/config.ts
new file mode 100644
index 000000000..2d2bb39c6
--- /dev/null
+++ b/pkgs/theme/src/config.ts
@@ -0,0 +1,78 @@
+import { AliasMap, BaseColors, HexString } from "./types.js";
+
+export type PaletteConfig = {
+ baseColors: BaseColors;
+ tones: number[];
+ aliases: AliasMap<"primary" | "secondary" | "error">;
+ common: {
+ // Black and white is always constant
+ // We declare this on the type level
+ white: "#ffffff";
+ black: "#000000";
+ // Some other color constants/reservation
+ [id: string]: HexString;
+ };
+};
+
+export const config: PaletteConfig = {
+ /** All color shades that are available
+ * This colors are used as "key colors" to generate a tonal palette from 0 to 100
+ * Steps are defined in 'tones'
+ */
+ baseColors: {
+ neutral: {
+ keyColor: "#807788",
+ tones: [98],
+ },
+ red: {
+ keyColor: "#e82439",
+ tones: [95],
+ },
+ green: {
+ keyColor: "#7AC51B",
+ tones: [98],
+ },
+ yellow: {
+ keyColor: "#E0E01F",
+ tones: [98],
+ },
+ purple: {
+ keyColor: "#661bc5",
+ tones: [],
+ },
+ blue: {
+ keyColor: "#1B7AC5",
+ tones: [95],
+ },
+ },
+
+ /** Common tones to generate out of all the baseColors
+ * number equals to the amount of light present in the color (HCT Color Space)
+ */
+ tones: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100],
+
+ /** create aliases from the color palette
+ *
+ * @example
+ *
+ * primary: "blue"
+ * ->
+ * ...
+ * primary40 -> blue40
+ * primary50 -> blue50
+ * ...
+ */
+ aliases: {
+ primary: "purple",
+ secondary: "green",
+ error: "red",
+ },
+ /** some color names are reserved
+ * typically those colors do not change when switching theme
+ * or are other types of constant in the UI
+ */
+ common: {
+ white: "#ffffff",
+ black: "#000000",
+ },
+};
diff --git a/pkgs/theme/src/generate.ts b/pkgs/theme/src/generate.ts
new file mode 100644
index 000000000..989406eb6
--- /dev/null
+++ b/pkgs/theme/src/generate.ts
@@ -0,0 +1,46 @@
+import { writeFile } from "fs";
+import palette from "./colors.json" assert { type: "json" };
+import { config } from "./config.js";
+
+type PaletteFile = typeof palette;
+
+const html = (palette: PaletteFile): string => {
+ const colors = Object.keys(config.baseColors).map((baseName) => {
+ const colors = Object.entries(palette.ref.palette)
+ .filter(([name, _]) => name.includes(baseName))
+ .sort((a, b) => {
+ return a[1].meta.color.shade - b[1].meta.color.shade;
+ })
+ .map(([key, color]) => {
+ console.log({ key, color });
+ return `${key}
`;
+ });
+ return `${colors.join(
+ "\n",
+ )}
`;
+ });
+
+ return `
+
+
+Page Title
+
+
+
+${colors.join("\n")}
+
+
+
+`;
+};
+
+writeFile("index.html", html(palette), (err) => {
+ if (err) {
+ console.error({ err });
+ } else {
+ console.log("Exported colors to html");
+ }
+});
diff --git a/pkgs/theme/src/main.ts b/pkgs/theme/src/main.ts
new file mode 100644
index 000000000..16cada9b6
--- /dev/null
+++ b/pkgs/theme/src/main.ts
@@ -0,0 +1,182 @@
+#!usr/bin/node
+import * as fs from "fs";
+import {
+ argbFromHex,
+ Hct,
+ hexFromArgb,
+} from "@material/material-color-utilities";
+import {
+ AliasTokenMap,
+ ColorDesignToken,
+ ColorSet,
+ HexString,
+ RefTokenSystem,
+ TonalPalette,
+ TonalPaletteConfig,
+ TonalPaletteItem,
+} from "./types.js";
+import { config } from "./config.js";
+
+const { baseColors, tones, aliases, common } = config;
+
+/** Takes a color, tone and name
+ * If a tone is given adjust the lightning level accordingly
+ *
+ * @returns TonalPaletteItem (meta wrapper around HCT)
+ */
+const getTonalPaletteItem = (
+ value: HexString,
+ name: string,
+ tone?: number,
+): TonalPaletteItem => {
+ const aRGB = argbFromHex(value);
+ const color = Hct.fromInt(aRGB);
+ if (tone !== undefined) {
+ color.tone = tone;
+ }
+ return {
+ shade: color.tone,
+ name: `${name || color.chroma}${Math.round(color.tone)}`,
+ baseName: name,
+ value: color,
+ };
+};
+
+/** create a flat list of the cross product from all colors and all tones.
+ *
+ * every color is mapped in the range from 0 to 100
+ * with the steps configure in `config.tones'
+ * additionally the key color is added unmodified
+ * lightning levels are rounded to the next natural number to form the 'name'
+ * Example:
+ *
+ * "blue" x [20.1, 30.3]
+ * ->
+ * [blue20, blue30]
+ */
+const mkTonalPalette =
+ (config: TonalPaletteConfig) =>
+ (name: string) =>
+ (keyTone: HexString): TonalPalette => {
+ const { tones } = config;
+ const aRGB = argbFromHex(keyTone);
+ const HctColor = Hct.fromInt(aRGB);
+ const roundedTone = Math.round(HctColor.tone * 100) / 100;
+
+ const localTones = [...tones, roundedTone];
+
+ return localTones.map((t) => getTonalPaletteItem(keyTone, name, t));
+ };
+
+/**
+ * Converts a PaletteItem into a hex color. (Wrapped)
+ * Adding meta attributes which avoids any information loss.
+ */
+const toDesignTokenContent = (color: TonalPaletteItem): ColorDesignToken => {
+ const { value } = color;
+ return {
+ type: "color",
+ value: hexFromArgb(value.toInt()),
+ meta: {
+ color,
+ date: new Date(),
+ },
+ };
+};
+
+const color: ColorSet = Object.entries(baseColors)
+ .map(([name, baseColor]) => ({
+ name,
+ baseColor,
+ tones: mkTonalPalette({
+ tones: [...tones, ...baseColor.tones].sort((a, b) => a - b),
+ })(name)(baseColor.keyColor),
+ }))
+ .reduce((acc, curr) => {
+ let currTones = curr.tones.reduce(
+ (o, v) => ({
+ ...o,
+ [v.name]: toDesignTokenContent(v),
+ }),
+ {},
+ );
+ return {
+ ...acc,
+ ...currTones,
+ };
+ }, {});
+
+/** Generate a set of tokens from a given alias mapping
+ *
+ * @param alias A string e.g. Primary -> Blue (Primary is the alias)
+ * @param name A string; Basename of the referenced value (e.g. Blue)
+ * @param colors A set of colors
+ * @returns All aliases from the given color set
+ */
+function resolveAlias(
+ alias: string,
+ name: string,
+ colors: ColorSet,
+): AliasTokenMap {
+ // All colors from the color map belonging to that single alias
+ // Example:
+ // Primary -> "blue"
+ // =>
+ // [ (blue0) , (blue10) , ..., (blue100) ]
+ const all = Object.values(colors)
+ .filter((n) => n.meta.color.name.includes(name))
+ .filter((n) => !n.meta.color.name.includes("."));
+
+ const tokens = all
+ .map((shade) => {
+ const shadeNumber = shade.meta.color.shade;
+ return {
+ name: `${alias}${Math.round(shadeNumber)}`,
+ value: { value: `{ref.palette.${shade.meta.color.name}}` },
+ // propagate the meta attribute of the actual value
+ meta: shade.meta,
+ };
+ })
+ // sort by tone
+ .sort((a, b) => a.meta.color.value.tone - b.meta.color.value.tone)
+ .reduce((acc, { name, value }) => ({ ...acc, [name]: value }), {});
+ return tokens;
+}
+
+const aliasMap = Object.entries(aliases).reduce(
+ (prev, [key, value]) => ({
+ ...prev,
+ ...resolveAlias(key, value, color),
+ }),
+ {},
+);
+
+const commonColors = Object.entries(common)
+ .map(([name, value]) =>
+ toDesignTokenContent(getTonalPaletteItem(value, name)),
+ )
+ .reduce(
+ (acc, val) => ({ ...acc, [val.meta.color.baseName]: val }),
+ {},
+ ) as ColorSet;
+
+const toPaletteToken = (color: ColorSet): RefTokenSystem => ({
+ ref: {
+ palette: color,
+ alias: aliasMap,
+ common: commonColors,
+ },
+});
+
+// Dump tokens to json file
+fs.writeFile(
+ "colors.json",
+ JSON.stringify(toPaletteToken(color), null, 2),
+ (err) => {
+ if (err) {
+ console.error({ err });
+ } else {
+ console.log("tokens successfully exported");
+ }
+ },
+);
diff --git a/pkgs/theme/src/types.ts b/pkgs/theme/src/types.ts
new file mode 100644
index 000000000..4a7a1888b
--- /dev/null
+++ b/pkgs/theme/src/types.ts
@@ -0,0 +1,90 @@
+import { Hct } from "@material/material-color-utilities";
+
+export type BaseColors = {
+ neutral: BaseColor;
+ red: BaseColor;
+ green: BaseColor;
+ yellow: BaseColor;
+ purple: BaseColor;
+ blue: BaseColor;
+};
+
+export type BaseColor = {
+ keyColor: HexString;
+ tones: number[];
+ follows?: string;
+};
+
+export type ColorSet = { [key: string]: ColorDesignToken };
+
+/** The resolved alias tokens
+ *
+ * @example
+ * {
+ * primary: "blue"
+ * ...
+ * }
+ *
+ */
+export type AliasMap = {
+ [alias in T]: keyof BaseColors;
+};
+
+/** The resolved alias tokens
+ *
+ * @example
+ * {
+ * primary0: "blue40"
+ * primary10: "blue40"
+ * ...
+ * primary100: "blue100"
+ * }
+ *
+ * Unfortunately My Typescript skills lack the ability to express this type any narrower :/
+ */
+export type AliasTokenMap = {
+ [alias: string]: { value: string };
+};
+
+export type TonalPaletteConfig = {
+ tones: number[];
+};
+
+export type HexString = string;
+
+export type TonalPaletteItem = {
+ /**
+ * @example
+ * 20
+ */
+ shade: number;
+ /**
+ * @example
+ * "blue20"
+ */
+ name: string;
+ /**
+ * @example
+ * "blue"
+ */
+ baseName: string;
+ value: Hct;
+};
+export type TonalPalette = TonalPaletteItem[];
+
+export type ColorDesignToken = {
+ type: "color";
+ value: HexString;
+ meta: {
+ color: TonalPaletteItem;
+ date: Date;
+ };
+};
+
+export type RefTokenSystem = {
+ ref: {
+ palette: ColorSet;
+ common: ColorSet;
+ alias: AliasTokenMap;
+ };
+};
diff --git a/pkgs/theme/tsconfig.json b/pkgs/theme/tsconfig.json
new file mode 100644
index 000000000..4b562c9c5
--- /dev/null
+++ b/pkgs/theme/tsconfig.json
@@ -0,0 +1,41 @@
+{
+ "include": ["src"],
+ "compilerOptions": {
+ "target": "ESNext" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
+
+ "lib": [
+ "ESNext",
+ "dom"
+ ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
+ "module": "NodeNext" /* Specify what module code is generated. */,
+ "rootDir": "src" /* Specify the root folder within your source files. */,
+ "moduleResolution": "nodenext" /* Specify how TypeScript looks up a file from a given module specifier. */,
+ "resolveJsonModule": true /* Enable importing .json files. */,
+ "outDir": "build",
+
+ "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
+
+ "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
+
+ /* Type Checking */
+ "strict": true /* Enable all strict type-checking options. */,
+ "noImplicitAny": true /* Enable error reporting for expressions and declarations with an implied 'any' type. */,
+ "strictNullChecks": true /* When type checking, take into account 'null' and 'undefined'. */,
+ "strictFunctionTypes": true /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */,
+ "strictBindCallApply": true /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */,
+ "strictPropertyInitialization": true /* Check for class properties that are declared but not set in the constructor. */,
+ "noImplicitThis": true /* Enable error reporting when 'this' is given the type 'any'. */,
+ // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
+ "alwaysStrict": true /* Ensure 'use strict' is always emitted. */,
+ "noUnusedLocals": true /* Enable error reporting when local variables aren't read. */,
+ "noUnusedParameters": true /* Raise an error when a function parameter isn't read. */,
+ "exactOptionalPropertyTypes": true /* Interpret optional property types as written, rather than adding 'undefined'. */,
+ "noImplicitReturns": true /* Enable error reporting for codepaths that do not explicitly return in a function. */,
+ "noFallthroughCasesInSwitch": true /* Enable error reporting for fallthrough cases in switch statements. */,
+ "noUncheckedIndexedAccess": true /* Add 'undefined' to a type when accessed using an index. */,
+ "noImplicitOverride": true /* Ensure overriding members in derived classes are marked with an override modifier. */,
+ "noPropertyAccessFromIndexSignature": true /* Enforces using indexed accessors for keys declared using an indexed type. */
+ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
+ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
+ }
+}
diff --git a/pkgs/ui/nix/floco-cfg.nix b/pkgs/ui/nix/floco-cfg.nix
index d8711270a..f0969327e 100644
--- a/pkgs/ui/nix/floco-cfg.nix
+++ b/pkgs/ui/nix/floco-cfg.nix
@@ -2,5 +2,6 @@
imports = [
./pdefs.nix
./foverrides.nix
+ ../../theme/nix/floco-cfg.nix
];
}
diff --git a/pkgs/ui/nix/foverrides.nix b/pkgs/ui/nix/foverrides.nix
index 3d4393eb6..5fb91a34c 100644
--- a/pkgs/ui/nix/foverrides.nix
+++ b/pkgs/ui/nix/foverrides.nix
@@ -100,6 +100,12 @@ in
optional = false;
dev = true;
};
+ "node_modules/@clan/colors" = {
+ key = "@clan/colors/1.0.0";
+ link = false;
+ optional = false;
+ dev = true;
+ };
};
});
in
diff --git a/pkgs/ui/src/app/theme/themes.ts b/pkgs/ui/src/app/theme/themes.ts
index edd2786ee..3da1b34d7 100644
--- a/pkgs/ui/src/app/theme/themes.ts
+++ b/pkgs/ui/src/app/theme/themes.ts
@@ -1,5 +1,7 @@
import { createTheme } from "@mui/material/styles";
+import colors from "@clan/colors/colors.json";
+
export const darkTheme = createTheme({
breakpoints: {
values: {
@@ -15,6 +17,7 @@ export const darkTheme = createTheme({
},
});
+const { palette, common } = colors.ref;
export const lightTheme = createTheme({
breakpoints: {
values: {
@@ -27,5 +30,27 @@ export const lightTheme = createTheme({
},
palette: {
mode: "light",
+ background: {
+ default: common.white.value,
+ paper: palette.neutral98.value,
+ },
+ primary: {
+ main: palette.green50.value,
+ },
+ secondary: {
+ main: palette.green50.value,
+ },
+ error: {
+ main: palette.red50.value,
+ },
+ warning: {
+ main: palette.yellow50.value,
+ },
+ success: {
+ main: palette.green50.value,
+ },
+ info: {
+ main: palette.red50.value,
+ },
},
});