Merge remote-tracking branch 'origin/main' into HEAD
All checks were successful
checks-impure / test (pull_request) Successful in 13s
checks / test (pull_request) Successful in 1m27s

This commit is contained in:
Jörg Thalheim 2023-10-03 17:32:19 +02:00
commit c0f6f78c9e
23 changed files with 3296 additions and 2 deletions

View File

@ -3,6 +3,7 @@
./clan-cli/flake-module.nix
./installer/flake-module.nix
./ui/flake-module.nix
./theme/flake-module.nix
];
perSystem = { pkgs, config, ... }: {

3
pkgs/theme/.editorconfig Normal file
View File

@ -0,0 +1,3 @@
[*.{js,jsx,ts,tsx,json}]
indent_style = space
indent_size = 4

12
pkgs/theme/.envrc Normal file
View File

@ -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 ''

43
pkgs/theme/.gitignore vendored Normal file
View File

@ -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/

41
pkgs/theme/default.nix Normal file
View File

@ -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;
}

View File

@ -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;
};
};
}

962
pkgs/theme/index.html Normal file
View File

@ -0,0 +1,962 @@
<!doctype html>
<html lang="en">
<meta charset="UTF-8" />
<title>Page Title</title>
<style></style>
<body>
<div
style="
display: grid;
grid-template-columns: repeat(13, minmax(0, 1fr));
gap: 1rem;
margin-bottom: 1rem;
"
>
<div
style="
background-color: #000000;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
neutral0
</div>
<div
style="
background-color: #1f1926;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
neutral10
</div>
<div
style="
background-color: #342e3c;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
neutral20
</div>
<div
style="
background-color: #4b4453;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
neutral30
</div>
<div
style="
background-color: #635b6b;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
neutral40
</div>
<div
style="
background-color: #7d7485;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
neutral50
</div>
<div
style="
background-color: #807788;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
neutral51
</div>
<div
style="
background-color: #978d9f;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
neutral60
</div>
<div
style="
background-color: #b2a7ba;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
neutral70
</div>
<div
style="
background-color: #cec2d5;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
neutral80
</div>
<div
style="
background-color: #eadef2;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
neutral90
</div>
<div
style="
background-color: #fff7ff;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
neutral98
</div>
<div
style="
background-color: #ffffff;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
neutral100
</div>
</div>
<div
style="
display: grid;
grid-template-columns: repeat(13, minmax(0, 1fr));
gap: 1rem;
margin-bottom: 1rem;
"
>
<div
style="
background-color: #000000;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
red0
</div>
<div
style="
background-color: #410006;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
red10
</div>
<div
style="
background-color: #680010;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
red20
</div>
<div
style="
background-color: #93001a;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
red30
</div>
<div
style="
background-color: #bf0026;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
red40
</div>
<div
style="
background-color: #e82439;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
red50
</div>
<div
style="
background-color: #ff5358;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
red60
</div>
<div
style="
background-color: #ff8986;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
red70
</div>
<div
style="
background-color: #ffb3b0;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
red80
</div>
<div
style="
background-color: #ffdad8;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
red90
</div>
<div
style="
background-color: #ffedeb;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
red95
</div>
<div
style="
background-color: #ffffff;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
red100
</div>
</div>
<div
style="
display: grid;
grid-template-columns: repeat(13, minmax(0, 1fr));
gap: 1rem;
margin-bottom: 1rem;
"
>
<div
style="
background-color: #000000;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
green0
</div>
<div
style="
background-color: #0f2000;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
green10
</div>
<div
style="
background-color: #1d3700;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
green20
</div>
<div
style="
background-color: #2d5000;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
green30
</div>
<div
style="
background-color: #3d6a00;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
green40
</div>
<div
style="
background-color: #4e8500;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
green50
</div>
<div
style="
background-color: #60a100;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
green60
</div>
<div
style="
background-color: #74be11;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
green70
</div>
<div
style="
background-color: #7ac51b;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
green72
</div>
<div
style="
background-color: #8edb34;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
green80
</div>
<div
style="
background-color: #a9f850;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
green90
</div>
<div
style="
background-color: #efffd8;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
green98
</div>
<div
style="
background-color: #ffffff;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
green100
</div>
</div>
<div
style="
display: grid;
grid-template-columns: repeat(13, minmax(0, 1fr));
gap: 1rem;
margin-bottom: 1rem;
"
>
<div
style="
background-color: #000000;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
yellow0
</div>
<div
style="
background-color: #1d1d00;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
yellow10
</div>
<div
style="
background-color: #323200;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
yellow20
</div>
<div
style="
background-color: #494900;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
yellow30
</div>
<div
style="
background-color: #626200;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
yellow40
</div>
<div
style="
background-color: #7b7b00;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
yellow50
</div>
<div
style="
background-color: #969600;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
yellow60
</div>
<div
style="
background-color: #b1b100;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
yellow70
</div>
<div
style="
background-color: #cdcd00;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
yellow80
</div>
<div
style="
background-color: #e0e01f;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
yellow87
</div>
<div
style="
background-color: #eaea2c;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
yellow90
</div>
<div
style="
background-color: #fffeac;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
yellow98
</div>
<div
style="
background-color: #ffffff;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
yellow100
</div>
</div>
<div
style="
display: grid;
grid-template-columns: repeat(13, minmax(0, 1fr));
gap: 1rem;
margin-bottom: 1rem;
"
>
<div
style="
background-color: #000000;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
purple0
</div>
<div
style="
background-color: #270057;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
purple10
</div>
<div
style="
background-color: #42008a;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
purple20
</div>
<div
style="
background-color: #5e07bd;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
purple30
</div>
<div
style="
background-color: #661bc5;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
purple33
</div>
<div
style="
background-color: #7734d6;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
purple40
</div>
<div
style="
background-color: #9152f1;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
purple50
</div>
<div
style="
background-color: #a974ff;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
purple60
</div>
<div
style="
background-color: #bf98ff;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
purple70
</div>
<div
style="
background-color: #d5baff;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
purple80
</div>
<div
style="
background-color: #ecdcff;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
purple90
</div>
<div
style="
background-color: #ffffff;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
purple100
</div>
</div>
<div
style="
display: grid;
grid-template-columns: repeat(13, minmax(0, 1fr));
gap: 1rem;
margin-bottom: 1rem;
"
>
<div
style="
background-color: #000000;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
blue0
</div>
<div
style="
background-color: #001d36;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
blue10
</div>
<div
style="
background-color: #003258;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
blue20
</div>
<div
style="
background-color: #00497c;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
blue30
</div>
<div
style="
background-color: #0061a3;
color: #fff;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
blue40
</div>
<div
style="
background-color: #1b7ac5;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
blue50
</div>
<div
style="
background-color: #4395e2;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
blue60
</div>
<div
style="
background-color: #62b0fe;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
blue70
</div>
<div
style="
background-color: #9ecaff;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
blue80
</div>
<div
style="
background-color: #d1e4ff;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
blue90
</div>
<div
style="
background-color: #e9f1ff;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
blue95
</div>
<div
style="
background-color: #ffffff;
color: #000;
height: 10rem;
border: solid 1px grey;
display: grid;
place-items: end;
"
>
blue100
</div>
</div>
</body>
</html>

View File

@ -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
++ [
];
}
# ---------------------------------------------------------------------------- #
#
#
#
# ============================================================================ #

View File

@ -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 ../.;
};
}

90
pkgs/theme/nix/pdefs.nix Normal file
View File

@ -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";
};
};
};
};
}

63
pkgs/theme/package-lock.json generated Normal file
View File

@ -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
}
}
}

23
pkgs/theme/package.json Normal file
View File

@ -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"
}
}

24
pkgs/theme/shell.nix Normal file
View File

@ -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"
'';
}

1507
pkgs/theme/src/colors.json Normal file

File diff suppressed because it is too large Load Diff

78
pkgs/theme/src/config.ts Normal file
View File

@ -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",
},
};

View File

@ -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 `<div style="background-color:${color.value}; color:${
color.meta.color.shade < 48 ? "#fff" : "#000"
}; height: 10rem; border:solid 1px grey; display:grid; place-items:end;">${key}</div>`;
});
return `<div style="display: grid; grid-template-columns: repeat(${13}, minmax(0, 1fr)); gap: 1rem; margin-bottom: 1rem">${colors.join(
"\n",
)}</div>`;
});
return `<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<title>Page Title</title>
<style>
</style>
<body>
${colors.join("\n")}
</body>
</html>
`;
};
writeFile("index.html", html(palette), (err) => {
if (err) {
console.error({ err });
} else {
console.log("Exported colors to html");
}
});

182
pkgs/theme/src/main.ts Normal file
View File

@ -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");
}
},
);

90
pkgs/theme/src/types.ts Normal file
View File

@ -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<T extends string> = {
[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;
};
};

41
pkgs/theme/tsconfig.json Normal file
View File

@ -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. */
}
}

View File

@ -2,5 +2,6 @@
imports = [
./pdefs.nix
./foverrides.nix
../../theme/nix/floco-cfg.nix
];
}

View File

@ -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

View File

@ -1,5 +1,5 @@
{ fetchzip }:
fetchzip {
url = "https://git.clan.lol/api/packages/clan/generic/ui/113xb8jl0ivhyazazy9c1qgmxy41rq1d382qzszbjrf0nv3qcys8/assets.tar.gz";
sha256 = "113xb8jl0ivhyazazy9c1qgmxy41rq1d382qzszbjrf0nv3qcys8";
url = "https://git.clan.lol/api/packages/clan/generic/ui/0l0hjjpvqyfiz5jk1yrqdhi50jc79v02fhdi7p7s39v1nfxzh3yn/assets.tar.gz";
sha256 = "0l0hjjpvqyfiz5jk1yrqdhi50jc79v02fhdi7p7s39v1nfxzh3yn";
}

View File

@ -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,
},
},
});