css-var-bind 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Jacob Berglund
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # css-var-bind
2
+
3
+ A lightweight web component that binds HTML input elements to CSS custom properties (variables).
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install css-var-bind
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```html
14
+ <css-var-bind variable="--scale" unit="px" target=":root" strategy="global">
15
+ <input type="range" min="0" max="100" value="50" />
16
+ <input type="number" min="0" max="100" value="50" />
17
+ </css-var-bind>
18
+ ```
19
+
20
+ The component automatically:
21
+
22
+ - Updates the CSS variable when inputs change
23
+ - Syncs values between multiple inputs
24
+ - Applies the unit to the value
25
+
26
+ ## Attributes
27
+
28
+ | Attribute | Required | Default | Description |
29
+ | ---------- | -------- | ----------- | --------------------------------------------------------------- |
30
+ | `variable` | Yes | - | CSS custom property name (must start with `--`) |
31
+ | `unit` | No | `""` | Unit to append (e.g., `px`, `rem`, `%`, `deg`) |
32
+ | `target` | No | - | CSS selector for the element to set the variable on |
33
+ | `strategy` | No | `"closest"` | How to resolve the target: `"closest"`, `"global"`, or `"self"` |
34
+
35
+ ### Strategy Options
36
+
37
+ - **`closest`** (default): Finds the nearest ancestor matching the `target` selector
38
+ - **`global`**: Uses `document.querySelector(target)` for global lookup
39
+ - **`self`**: Sets the variable on the component itself (ignores `target`)
40
+
41
+ ## Examples
42
+
43
+ **Color picker:**
44
+
45
+ ```html
46
+ <css-var-bind target="body" variable="--bg-color">
47
+ <input type="color" value="#b4d455" />
48
+ </css-var-bind>
49
+ ```
50
+
51
+ **Scoped to ancestor:**
52
+
53
+ ```html
54
+ <div class="card">
55
+ <css-var-bind variable="--card-padding" unit="rem" target=".card">
56
+ <input type="number" min="0" max="5" value="2" />
57
+ </css-var-bind>
58
+ </div>
59
+ ```
60
+
61
+ **Self-binding:**
62
+
63
+ ```html
64
+ <css-var-bind variable="--opacity" strategy="self">
65
+ <input type="range" min="0" max="1" step="0.1" value="1" />
66
+ </css-var-bind>
67
+ ```
68
+
69
+ ## License
70
+
71
+ MIT
@@ -0,0 +1,56 @@
1
+ /**
2
+ * A custom element that binds input elements to a CSS variable.
3
+ * It also syncs values between different inputs, like range and number.
4
+ *
5
+ * @element css-var-bind
6
+ *
7
+ * @attr {string} variable - **Required.** The CSS custom property name to bind to.
8
+ * Must start with `--` (e.g., `--scale`, `--primary-color`).
9
+ *
10
+ * @attr {string} [unit=""] - Optional unit to append to the value when setting the CSS variable.
11
+ * Common values: `px`, `%`, `em`, `rem`, `deg`, etc.
12
+ *
13
+ * @attr {string} [target] - CSS selector for the element on which to set the CSS variable.
14
+ * How this selector is resolved depends on the `strategy` attribute.
15
+ *
16
+ * @attr {"closest"|"global"|"self"} [strategy="closest"] - How to resolve the target selector.
17
+ * - `"closest"` — Uses `element.closest(target)` to find an ancestor (default).
18
+ * - `"global"` — Uses `document.querySelector(target)` for global lookup.
19
+ * - `"self"` — Ignores `target` and binds to the component itself.
20
+ *
21
+ * @example
22
+ * ```html
23
+ * <!-- Bind to a global element (document.querySelector) -->
24
+ * <css-var-bind variable="--scale" unit="px" target=":root" strategy="global">
25
+ * <input type="range" min="0" max="100" />
26
+ * <input type="number" />
27
+ * </css-var-bind>
28
+ *
29
+ * <!-- Bind to the closest ancestor with a class (default strategy) -->
30
+ * <div class="card">
31
+ * <css-var-bind variable="--card-padding" unit="rem" target=".card">
32
+ * <input type="number" min="0" max="5" />
33
+ * </css-var-bind>
34
+ * </div>
35
+ *
36
+ * <!-- Bind to the component itself -->
37
+ * <css-var-bind variable="--opacity" strategy="self">
38
+ * <input type="range" min="0" max="1" step="0.1" />
39
+ * </css-var-bind>
40
+ * ```
41
+ */
42
+ declare class CssVarBind extends HTMLElement {
43
+ bindToElement: HTMLElement | null;
44
+ cssVariableName: string;
45
+ inputs: NodeListOf<HTMLInputElement> | null;
46
+ unit: string;
47
+ private boundHandleInput;
48
+ constructor();
49
+ connectedCallback(): void;
50
+ disconnectedCallback(): void;
51
+ private handleInput;
52
+ handleSteppedInputs(target: HTMLInputElement): void;
53
+ setValueIfCSSPropertyExists(): void;
54
+ syncInputs(changedInput: HTMLInputElement): void;
55
+ }
56
+ export default CssVarBind;
package/dist/index.js ADDED
@@ -0,0 +1,140 @@
1
+ /**
2
+ * A custom element that binds input elements to a CSS variable.
3
+ * It also syncs values between different inputs, like range and number.
4
+ *
5
+ * @element css-var-bind
6
+ *
7
+ * @attr {string} variable - **Required.** The CSS custom property name to bind to.
8
+ * Must start with `--` (e.g., `--scale`, `--primary-color`).
9
+ *
10
+ * @attr {string} [unit=""] - Optional unit to append to the value when setting the CSS variable.
11
+ * Common values: `px`, `%`, `em`, `rem`, `deg`, etc.
12
+ *
13
+ * @attr {string} [target] - CSS selector for the element on which to set the CSS variable.
14
+ * How this selector is resolved depends on the `strategy` attribute.
15
+ *
16
+ * @attr {"closest"|"global"|"self"} [strategy="closest"] - How to resolve the target selector.
17
+ * - `"closest"` — Uses `element.closest(target)` to find an ancestor (default).
18
+ * - `"global"` — Uses `document.querySelector(target)` for global lookup.
19
+ * - `"self"` — Ignores `target` and binds to the component itself.
20
+ *
21
+ * @example
22
+ * ```html
23
+ * <!-- Bind to a global element (document.querySelector) -->
24
+ * <css-var-bind variable="--scale" unit="px" target=":root" strategy="global">
25
+ * <input type="range" min="0" max="100" />
26
+ * <input type="number" />
27
+ * </css-var-bind>
28
+ *
29
+ * <!-- Bind to the closest ancestor with a class (default strategy) -->
30
+ * <div class="card">
31
+ * <css-var-bind variable="--card-padding" unit="rem" target=".card">
32
+ * <input type="number" min="0" max="5" />
33
+ * </css-var-bind>
34
+ * </div>
35
+ *
36
+ * <!-- Bind to the component itself -->
37
+ * <css-var-bind variable="--opacity" strategy="self">
38
+ * <input type="range" min="0" max="1" step="0.1" />
39
+ * </css-var-bind>
40
+ * ```
41
+ */
42
+ class CssVarBind extends HTMLElement {
43
+ bindToElement = null;
44
+ cssVariableName = "";
45
+ inputs = null;
46
+ unit = "";
47
+ boundHandleInput;
48
+ constructor() {
49
+ super();
50
+ this.boundHandleInput = this.handleInput.bind(this);
51
+ }
52
+ connectedCallback() {
53
+ this.cssVariableName = this.getAttribute("variable") || "";
54
+ this.unit = this.getAttribute("unit") || "";
55
+ this.inputs = this.querySelectorAll("input");
56
+ if (!this.cssVariableName.startsWith("--")) {
57
+ console.error("CSS variable name must start with --");
58
+ return;
59
+ }
60
+ if (!this.inputs.length) {
61
+ console.warn("css-var-bind: No input elements found");
62
+ return;
63
+ }
64
+ const target = this.getAttribute("target");
65
+ const strategy = this.getAttribute("strategy") || "closest";
66
+ switch (strategy) {
67
+ case "global":
68
+ this.bindToElement = target ? document.querySelector(target) : null;
69
+ break;
70
+ case "self":
71
+ this.bindToElement = this;
72
+ break;
73
+ default:
74
+ this.bindToElement = target ? this.closest(target) : this;
75
+ break;
76
+ }
77
+ if (!this.bindToElement) {
78
+ console.warn(`css-var-bind: Target element "${target}" not found`);
79
+ return;
80
+ }
81
+ this.inputs.forEach((input) => {
82
+ this.setValueIfCSSPropertyExists();
83
+ input.addEventListener("input", this.boundHandleInput);
84
+ });
85
+ }
86
+ disconnectedCallback() {
87
+ if (this.inputs) {
88
+ this.inputs.forEach((input) => {
89
+ input.removeEventListener("input", this.boundHandleInput);
90
+ });
91
+ }
92
+ }
93
+ handleInput(event) {
94
+ if (!(event.target instanceof HTMLInputElement))
95
+ return;
96
+ this.handleSteppedInputs(event.target);
97
+ }
98
+ handleSteppedInputs(target) {
99
+ if (!this.bindToElement)
100
+ return;
101
+ this.bindToElement.style.setProperty(this.cssVariableName, target.value + this.unit);
102
+ this.syncInputs(target);
103
+ }
104
+ setValueIfCSSPropertyExists() {
105
+ if (!this.bindToElement || !this.inputs)
106
+ return;
107
+ const root = getComputedStyle(this.bindToElement);
108
+ this.inputs.forEach((input) => {
109
+ if (input.type === "radio")
110
+ return;
111
+ if (input.type === "range") {
112
+ const cssValue = root.getPropertyValue(this.cssVariableName);
113
+ if (!cssValue) {
114
+ console.warn(`css-var-bind: CSS variable ${this.cssVariableName} is not set on the target element`);
115
+ }
116
+ else {
117
+ input.value = cssValue;
118
+ }
119
+ }
120
+ if (input.value) {
121
+ // if input has a value already, set the css variable to that value
122
+ this.bindToElement?.style.setProperty(this.cssVariableName, input.value + this.unit);
123
+ }
124
+ if (input.value === "") {
125
+ input.value = root.getPropertyValue(this.cssVariableName);
126
+ }
127
+ });
128
+ }
129
+ syncInputs(changedInput) {
130
+ if (!this.inputs)
131
+ return;
132
+ this.inputs.forEach((input) => {
133
+ if (input !== changedInput) {
134
+ input.value = changedInput.value;
135
+ }
136
+ });
137
+ }
138
+ }
139
+ export default CssVarBind;
140
+ customElements.define("css-var-bind", CssVarBind);
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "css-var-bind",
3
+ "version": "0.0.1",
4
+ "description": "A utility web component which binds (and syncs) inputs to CSS variables",
5
+ "keywords": [
6
+ "web-components",
7
+ "css-variables",
8
+ "custom-elements",
9
+ "css-custom-properties",
10
+ "input-binding",
11
+ "reactive",
12
+ "vanilla-js"
13
+ ],
14
+ "author": "Jacob Berglund",
15
+ "license": "MIT",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/jberglund/css-var-bind.git"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/jberglund/css-var-bind/issues"
22
+ },
23
+ "homepage": "https://github.com/jberglund/css-var-bind#readme",
24
+ "type": "module",
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/index.d.ts",
28
+ "default": "./dist/index.js"
29
+ }
30
+ },
31
+ "files": [
32
+ "dist"
33
+ ],
34
+ "scripts": {
35
+ "build": "bun run tsc",
36
+ "lint": "biome check .",
37
+ "lint:fix": "biome check --write .",
38
+ "publint": "bunx publint",
39
+ "dev": "bun index.html",
40
+ "prepublishOnly": "bun run lint && bun run build"
41
+ },
42
+ "devDependencies": {
43
+ "@biomejs/biome": "^2.3.14",
44
+ "typescript": "^5.9.3"
45
+ }
46
+ }