Skip to content

Commit 504f728

Browse files
committed
v0breaking: complete rewrite
Complete rewrite. - Simplified everything. No more type soup. Lost a few features, but greatly improved overall usability. - Added integration with tailwind (I have been converted). - Made intergration with custom interpolators easier. - Added ability to use colors stops with InterpolatedVars.
1 parent 2b1264a commit 504f728

36 files changed

+1380
-1748
lines changed

.github/workflows/build-only.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
runs-on: ubuntu-latest
1717
strategy:
1818
matrix:
19-
node-version: ["lts/-2", "latest"]
19+
node-version: ["lts/-1", "latest"]
2020

2121
steps:
2222

.github/workflows/build.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
runs-on: ubuntu-latest
1717
strategy:
1818
matrix:
19-
node-version: ["lts/-2", "latest"]
19+
node-version: ["lts/-1", "latest"]
2020

2121
steps:
2222

.github/workflows/release.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
runs-on: ubuntu-latest
1717
strategy:
1818
matrix:
19-
node-version: ["lts/-2"]
19+
node-version: ["lts/-1"]
2020

2121
steps:
2222

.husky/pre-commit

-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,4 @@
33

44
echo "Running pre-commit hook"
55

6-
# TOCONFIGURE
76
npm run build && npm run lint && npm run test

.husky/pre-push

-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,4 @@
33

44
echo "Running pre-push hook"
55

6-
# TOCONFIGURE
76
npm run lint:commits

README.md

+122-44
Original file line numberDiff line numberDiff line change
@@ -6,75 +6,153 @@
66

77
# [Docs](https://alanscodelog.github.io/metamorphosis)
88

9-
Metamorphosis is a css variable management library that helps create and organize variables into easily configurable groups and themes.
9+
Metamorphosis is a css variable management library that helps create and organize variables into easily configurable themes.
1010

11-
Establish a few control variables and convert them into an easy to use design system.
11+
Establish a few control variables and easily create interpolated variables with multiple stops.
1212

13-
Unlike other css libraries, it's designed to be managed from the js side to allow for easy, consistent user theming. All variables are also strongly typed with typescript.
13+
It can be used by itself or as a compliment to other css libraries (e.g. tailwind). It's also easy to integrate other interpolators such as colorsjs.io.
14+
15+
It's designed to allow management from the js side to allow for easy, consistent user theming in apps (though it can be used to statically generate css variables as well).
16+
17+
All variables are strongly typed with typescript.
1418

1519
# Usage
1620

1721
A quick example:
1822

1923
```ts
20-
import { Format, InterpolatedVars, Theme, Unit, Var, VarGroup } from "metamorphosis"
21-
22-
// create the base color variables
23-
const white = new Var("white", Unit.rgb, { r: 255, g: 255, b: 255 }, {
24-
format: Format.rgb,
25-
})
26-
const black = new Var("black", Unit.rgb, { r: 0, g: 0, b: 0 }, {
27-
format: Format.rgb,
28-
})
29-
30-
// create interpolated variables based on them
31-
const grays = new InterpolatedVars("gray", Unit.rgb, {
32-
start: white,
33-
end: black,
34-
steps: 10,
35-
format: Format.rgb
36-
})
37-
38-
// further group/select certain variables
39-
const colors = new VarGroup("color", {
40-
text: [grays, "8"]
41-
})
42-
43-
// create a theme
44-
const theme = new Theme("mainTheme", {
24+
const paddingMin = new ControlVar(Units.px, 0)
25+
const paddingMax = new ControlVar(Units.px, 200)
26+
27+
28+
const padding = new InterpolatedVars("padding",
29+
Units.px,
30+
[paddingMin, paddingMax],
31+
/* { steps: 10 } i.e. 0-9 */
32+
)
33+
34+
const white = new ControlVar(Units.rgb, { r: 255, g: 255, b: 255 })
35+
const black = new ControlVar(Units.rgb, { r: 0, g: 0, b: 0 })
36+
37+
const grays = new InterpolatedVars("gray", Units.rgb, [white, black],
38+
{
39+
// custom percentages
40+
steps: [0, 0.2, 0.8, 1],
41+
keyName: paddedKeyNamer(1000), // gray-000
42+
})
43+
44+
const lightRed = new ControlVar(Units.rgb, { r: 255, g: 0, b: 0 })
45+
const saturatedMiddleRed = new ControlVar(Units.rgb, { r: 255, g: 170, b: 170 })
46+
const darkRed = new ControlVar(Units.rgb, { r: 50, g: 0, b: 0 })
47+
48+
// interpolated along multiple values
49+
const red = new InterpolatedVars("red", Units.rgb, [
50+
lightRed,
51+
saturatedMiddleRed,
52+
darkRed,
53+
])
54+
55+
// with custom stops
56+
const reds = new InterpolatedVars("red", Units.rgb, [
57+
[0, lightRed],
58+
[0.25, saturatedMiddleRed],
59+
[0.75, darkRed],
60+
])
61+
62+
63+
const theme = new Theme({
4564
grays,
46-
colors
65+
padding,
4766
})
4867

49-
// view theme
50-
// by default dependencies are hidden, white and black won't show
51-
console.log(theme.css())
68+
// theme.css
5269
// :root {
53-
// gray-0: rgb(255, 255, 255);
54-
// ...
55-
// gray-10: rgb(0, 0, 0);
56-
// color-text: rgb(51, 51, 51);
70+
// --gray-000: rgb(255, 255, 255);
71+
// --gray-250: rgb(204, 204, 204);
72+
// --gray-500: rgb(51, 51, 51);
73+
// --gray-750: rgb(0, 0, 0);
74+
// --padding-0: 0px;
75+
// ...
76+
// --padding-9: 200px;
5777
// }
5878

5979
// change a control variable
6080
white.set({ r: 200, g: 200, b: 200 })
6181

6282
// all variables that depend on it update
63-
console.log(theme.css())
83+
// theme.css
6484
// :root {
65-
// gray-0: rgb(200, 200, 200);
85+
// --gray-000: rgb(200, 200, 200);
6686
// ...
67-
// gray-10: rgb(0, 0, 0);
68-
// color-text: rgb(40, 40, 40);
87+
// --gray-750: rgb(0, 0, 0);
6988
// }
7089

71-
// attach or detach a theme from an element, if none given, attaches to document.documentElement
90+
// attach or detach a theme from an element
91+
// if none given attaches to document.documentElement
7292
// attaching will set the css variables on the element and keep them updated
7393
theme.attach(/* el */)
7494
theme.detach(/* el */)
75-
```
7695

77-
Usage of each class is detailed in the docs.
96+
// add/remove variables
97+
theme.add({ reds })
98+
theme.add({ lightRed })
99+
theme.remove("lightRed")
100+
101+
// ADVANCED
102+
103+
// custom unit creation
104+
// the function argument is used as the unit definition
105+
const fancyRem = Units.createSimpleUnit("fancy-rem") // same as ({ _ }: { _: number }) => `${_}fancy-rem`
106+
const fancyUnit = (
107+
{ some, fancy, unit }:
108+
{ some: number, fancy: "string", unit: boolean }
109+
) => `${some}px ${fancy} ${unit}`
110+
111+
// so long as the control vars take the same arguments
112+
// you can use unit functions as custom formatters:
113+
const customRgbFormatter = ({ r, g, b, a = 0 }: Units.Rgb) => `R:${r} G:${g} B:${b} ${a ? `A:${a}` : ""}`
114+
const grays3 = new InterpolatedVars("gray", customRgbFormatter, [white, black])
115+
116+
// custom interpolation, for example, using colorjs.io
117+
118+
// import Color from "colorjs.io"
119+
120+
const grays4 = new InterpolatedVars("gray", Units.rgb, [white, black], {
121+
interpolator: ({ percent, state, start, end }) => {
122+
const key = start.css + end.css
123+
// re/create state if at start or if key switched (due to
124+
// multiple stops)
125+
if (state.key !== key) {
126+
state.range = new Color(start.css).range(new Color(end.css), { space: "srgb" })
127+
state.key = key
128+
}
129+
130+
const val = state.range(percent).coords
131+
return { r: val[0] * 255, g: val[1] * 255, b: val[0] * 255 }
132+
},
133+
})
134+
135+
// we can also be a bit more flexible at the cost of strict typing and
136+
// use colorjs.io's parsing abilities to pass the colors as strings in any format
137+
138+
const white2 = new ControlVar(Units.str, `rgb(255, 255, 255)`)
139+
const black2 = new ControlVar(Units.str, `#000000`)
140+
141+
const grays5 = new InterpolatedVars("gray", Units.str, [white2, black2], {
142+
interpolator: ({ percent, state, start, end }) => {
143+
const key = start.css + end.css
144+
if (state.key !== key) {
145+
state.range = new Color(start.css).range(new Color(end.css), { space: "srgb" })
146+
state.key = key
147+
}
148+
/* ... */
149+
return state.range(percent)
150+
.to("srgb") // our preferred output space
151+
.toString({ format: "srgb" })
152+
},
153+
})
154+
```
78155

79-
For an example of more advanced usage, see [the example base theme](https://github.com/AlansCodeLog/metamorphosis/blob/master/src/BaseTheme/BaseTheme.ts) .
156+
For an example of more advanced usage, see [the example base theme](https://github.com/AlansCodeLog/metamorphosis/blob/master/src/BaseTheme.ts).
80157

158+
The library has optional peer dependencies for tailwind and colorjs.io. colorjs.io is only needed if importing the base theme from `metamorphosis/BaseTheme`. And tailwind is only needed if importing from `metamorphosis/tailwind`.

package.json

+29-29
Original file line numberDiff line numberDiff line change
@@ -2,87 +2,89 @@
22
"name": "metamorphosis",
33
"description": "Helps create css variables and turn them into easily configurable, reactive themes.",
44
"version": "0.0.0-semantically-released",
5-
"module": "./dist/index.js",
65
"type": "module",
6+
"module": "./dist/index.js",
77
"exports": {
8-
".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" },
9-
"./*": { "types": "./dist/*/index.d.ts", "import": "./dist/*/index.js" }
8+
".": {
9+
"types": "./dist/index.d.ts",
10+
"import": "./dist/index.js"
11+
},
12+
"./*": {
13+
"types": "./dist/*.d.ts",
14+
"import": "./dist/*.js"
15+
}
1016
},
1117
"scripts": {
1218
"debug": "ts-node -r tsconfig-paths/register -T --esm",
13-
1419
"build": "vite build",
1520
"build:dev": "vite build --mode development",
1621
"build:watch": "vite build --watch --mode production",
1722
"build:types": "tsc -p tsconfig.types.json && npm run build:types:fix",
1823
"build:types:fix": "tsc-alias -p tsconfig.types.json --debug",
19-
2024
"lint:eslint": "eslint \"{src,tests,bin}/**/*.{cjs,js,ts}\" \"*.{cjs,js,ts}\" --max-warnings=0 --report-unused-disable-directives",
2125
"lint:types": "tsc --noEmit --pretty",
2226
"lint:commits": "commitlint --from $(git rev-list HEAD --not --remotes | tail -1)^ --to HEAD --verbose",
2327
"lint:imports": "madge --circular --extensions ts ./src",
24-
"lint": "npm run lint:types && npm run lint:eslint",
25-
28+
"lint": "npm run lint:types && npm run lint:eslint && npm lint:imports",
2629
"coverage": "vitest --coverage",
2730
"coverage:dev": "vitest --watch --coverage",
28-
2931
"test": "npm run lint:types && vitest run",
3032
"test:watch": "vitest --watch",
31-
"test:inspect-errors": "cross-env INSPECT_ERRORS=true npm run test",
32-
3333
"doc": "typedoc --options typedoc.config.cjs",
3434
"doc:watch": "onchange -i \"src/**/*.ts\" \"typedoc.config.cjs\" -- npm run doc",
3535
"doc:serve": "http-server docs --port=5001",
3636
"doc:dev": "concurrently \"npm run doc:watch\" \"npm run doc:serve\"",
3737
"doc:check-invalid": "typedoc --options typedoc.config.cjs --listInvalidSymbolLinks",
38-
3938
"actions:debug": "act -r -v -j build-only",
40-
"prepare": "husky install && npm run build",
41-
"gen:exports": "indexit update --ignore **.d.ts {Unit,Format}.ts -o '${path}.js'"
39+
"prepare": "npm exec husky install && npm run build",
40+
"gen:exports": "indexit update --ignore {Units,utils,internal}.ts -o '${path}.js'"
4241
},
4342
"dependencies": {
44-
"@alanscodelog/utils": "^4.0.0-beta.2",
45-
"core-js":"^3.30.1"
43+
"@alanscodelog/utils": "^4.0.0-beta.4"
44+
},
45+
"peerDependencies": {
46+
"colorjs.io": "^0.4.3",
47+
"tailwindcss": "^3.3.2"
48+
},
49+
"peerDependenciesMeta": {
50+
"colorjs.io": { "optional": true },
51+
"tailwindcss": { "optional": true }
4652
},
4753
"devDependencies": {
4854
"@alanscodelog/commitlint-config": "^2.0.0",
4955
"@alanscodelog/eslint-config": "^4.0.2",
5056
"@alanscodelog/semantic-release-config": "^2.0.1",
5157
"@alanscodelog/tsconfigs": "^3.1.3",
52-
"@babel/core": "^7.21.5",
53-
"@babel/plugin-transform-runtime": "^7.21.4",
54-
"@babel/preset-env": "^7.21.5",
55-
"@babel/runtime": "^7.21.5",
56-
"@rollup/plugin-babel": "^6.0.3",
5758
"@types/node": "^18.16.3",
5859
"@typescript-eslint/eslint-plugin": "^5.59.1",
5960
"@typescript-eslint/parser": "^5.59.1",
6061
"@vitest/coverage-c8": "^0.30.1",
6162
"@vue/eslint-config-typescript": "^11.0.3",
63+
"colorjs.io": "^0.4.3",
6264
"commitlint": "^17.6.1",
6365
"concurrently": "^8.0.1",
6466
"cross-env": "^7.0.3",
67+
"eslint": "^8.39.0",
6568
"eslint-import-resolver-typescript": "^3.5.5",
6669
"eslint-plugin-import": "^2.27.5",
6770
"eslint-plugin-jsdoc": "^39.6.4",
6871
"eslint-plugin-simple-import-sort": "^10.0.0",
6972
"eslint-plugin-vue": "^9.11.0",
70-
"eslint": "^8.39.0",
7173
"fast-glob": "^3.2.12",
7274
"http-server": "^14.1.1",
7375
"husky": "^8.0.3",
74-
"indexit":"beta",
75-
"jsdom":"^21.1.1",
76-
"madge":"^6.0.0",
76+
"indexit": "beta",
77+
"jsdom": "^21.1.1",
78+
"madge": "^6.0.0",
7779
"onchange": "^7.1.0",
7880
"semantic-release": "^21.0.2",
81+
"tailwindcss": "^3.3.2",
7982
"ts-node": "^10.9.1",
8083
"tsc-alias": "^1.8.6",
81-
"type-fest":"^3.9.0",
8284
"typedoc": "^0.24.6",
8385
"typescript": "^5.0.4",
84-
"vite-tsconfig-paths":"^4.2.0",
8586
"vite": "^4.3.3",
87+
"vite-tsconfig-paths": "^4.2.0",
8688
"vitest": "^0.30.1"
8789
},
8890
"author": "Alan <alanscodelog@gmail.com>",
@@ -94,17 +96,15 @@
9496
],
9597
"release": { "extends": [ "@alanscodelog/semantic-release-config" ] },
9698
"commitlint": { "extends": [ "@alanscodelog" ] },
97-
"browserslist":"defaults and supports es6-module,maintained node versions",
9899
"engines": { "node": ">=18" },
99100
"@comments": {
101+
"peerDependencies": "colorsjs.io is only needed if using the base theme",
100102
"scripts": {
101103
"test": "Runs `lint:types` before (so that flags can be passed to the test command) so that we can test type assertions. See expect_type function in @alanscodelog/utils.",
102104
"lint:commits": "Lints all unpushed commits in the active branch.",
103-
"test:inspect-errors": "For use with my inspect_error utility function from @alanscodelog/utils",
104105
"prepare": "Needed so that if we pull the package from git it will get built and installed properly.",
105106
"actions:debug": "For debugging github build action locally with nektos/act. Requires act and docker. Note: Cache will never work locally because of https://github.com/nektos/act/issues/285"
106107
}
107108
},
108109
"private": false
109110
}
110-

0 commit comments

Comments
 (0)