typography-controller 1.0.3 → 1.1.0

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/README.md CHANGED
@@ -15,6 +15,7 @@ Adjust font size, letter spacing, word spacing, line height, contrast, and font
15
15
  - Font Family: Choose from predefined fonts (Arial, Georgia, Courier New, Verdana).
16
16
  - Framework‑agnostic — Works in HTML, React, Vue, Svelte, etc.
17
17
  - Public API — getValues(), setValues(), setFeatures(), and change events.
18
+ - EAA - This component is designed with full EAA‑aligned accessibility practices, including keyboard navigation, ARIA labeling, and perceivable focus states.
18
19
 
19
20
  ## 📦 Installation
20
21
 
@@ -1,6 +1,6 @@
1
- import { createComponent as o } from "@lit/react";
2
- import a from "react";
3
- let n = class extends HTMLElement {
1
+ import { createComponent as a } from "@lit/react";
2
+ import o from "react";
3
+ let r = class extends HTMLElement {
4
4
  constructor() {
5
5
  super(), this.attachShadow({ mode: "open" }), this.shadowRoot.innerHTML = `
6
6
  <style>
@@ -95,78 +95,167 @@ let n = class extends HTMLElement {
95
95
  }
96
96
 
97
97
  select:focus {
98
+ outline: none;
99
+ box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.35);
100
+ border-radius: 999px;
101
+ }
102
+
103
+ /* Highlight the track when focused */
104
+ input[type="range"]:focus {
105
+ outline: none;
106
+ box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.35);
107
+ border-radius: 999px;
108
+ }
109
+
110
+ /* Glow effect on the thumb when focused */
111
+ input[type="range"]:focus::-webkit-slider-thumb {
112
+ box-shadow:
113
+ 0 0 0 4px rgba(37, 99, 235, 0.35),
114
+ 0 4px 10px rgba(0, 0, 0, 0.25);
115
+ border-color: #2563eb;
116
+ }
117
+
118
+ /* Firefox thumb focus */
119
+ input[type="range"]::-moz-range-thumb:focus {
120
+ box-shadow:
121
+ 0 0 0 4px rgba(37, 99, 235, 0.35),
122
+ 0 4px 10px rgba(0, 0, 0, 0.25);
98
123
  border-color: #2563eb;
99
- box-shadow: 0 0 0 1px rgba(37, 99, 235, 0.4);
124
+ }
125
+ input[type="range"]:hover::-webkit-slider-thumb {
126
+ transform: scale(1.05);
127
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
100
128
  }
101
129
  </style>
102
130
 
103
131
  <div class="header">
104
- <div class="title">Typography Controls</div>
105
- <div class="badge">Web Component</div>
132
+ <h2 class="title" id="controllerTitle">Typography Controls</h2>
133
+ <span class="badge">v1.0.0</span>
106
134
  </div>
107
135
 
108
- <div class="group" id="groupFontSize">
136
+ <!-- Live region for announcements -->
137
+ <div aria-live="polite" class="sr-only" id="liveRegion"></div>
138
+
139
+ <div class="group" id="groupFontSize" role="group" aria-labelledby="labelFontSize">
109
140
  <div class="label-row">
110
- <label for="fontSize">Font size</label>
111
- <span class="value" id="fontSizeValue"></span>
141
+ <label id="labelFontSize" for="fontSize">Font size</label>
142
+ <span class="value" id="fontSizeValue" aria-hidden="true"></span>
112
143
  </div>
113
- <input type="range" id="fontSize" min="12" max="60" value="24">
144
+
145
+ <input
146
+ type="range"
147
+ id="fontSize"
148
+ min="12"
149
+ max="60"
150
+ value="24"
151
+ aria-valuemin="12"
152
+ aria-valuemax="60"
153
+ aria-valuenow="24"
154
+ aria-labelledby="labelFontSize"
155
+ >
114
156
  </div>
115
157
 
116
- <div class="group" id="groupLetterSpacing">
158
+ <div class="group" id="groupLetterSpacing" role="group" aria-labelledby="labelLetterSpacing">
117
159
  <div class="label-row">
118
- <label for="letterSpacing">Letter spacing</label>
119
- <span class="value" id="letterSpacingValue"></span>
160
+ <label id="labelLetterSpacing" for="letterSpacing">Letter spacing</label>
161
+ <span class="value" id="letterSpacingValue" aria-hidden="true"></span>
120
162
  </div>
121
- <input type="range" id="letterSpacing" min="0" max="20" value="0">
163
+
164
+ <input
165
+ type="range"
166
+ id="letterSpacing"
167
+ min="0"
168
+ max="20"
169
+ value="0"
170
+ aria-valuemin="0"
171
+ aria-valuemax="20"
172
+ aria-valuenow="0"
173
+ aria-labelledby="labelLetterSpacing"
174
+ >
122
175
  </div>
123
176
 
124
- <div class="group" id="groupWordSpacing">
177
+ <div class="group" id="groupWordSpacing" role="group" aria-labelledby="labelWordSpacing">
125
178
  <div class="label-row">
126
- <label for="wordSpacing">Word spacing</label>
127
- <span class="value" id="wordSpacingValue"></span>
179
+ <label id="labelWordSpacing" for="wordSpacing">Word spacing</label>
180
+ <span class="value" id="wordSpacingValue" aria-hidden="true"></span>
128
181
  </div>
129
- <input type="range" id="wordSpacing" min="0" max="40" value="0">
182
+
183
+ <input
184
+ type="range"
185
+ id="wordSpacing"
186
+ min="0"
187
+ max="40"
188
+ value="0"
189
+ aria-valuemin="0"
190
+ aria-valuemax="40"
191
+ aria-valuenow="0"
192
+ aria-labelledby="labelWordSpacing"
193
+ >
130
194
  </div>
131
195
 
132
- <div class="group" id="groupLineHeight">
196
+ <div class="group" id="groupLineHeight" role="group" aria-labelledby="labelLineHeight">
133
197
  <div class="label-row">
134
- <label for="lineHeight">Line height</label>
135
- <span class="value" id="lineHeightValue"></span>
198
+ <label id="labelLineHeight" for="lineHeight">Line height</label>
199
+ <span class="value" id="lineHeightValue" aria-hidden="true"></span>
136
200
  </div>
137
- <input type="range" id="lineHeight" min="1" max="3" step="0.1" value="1.5">
201
+
202
+ <input
203
+ type="range"
204
+ id="lineHeight"
205
+ min="1"
206
+ max="3"
207
+ step="0.1"
208
+ value="1.5"
209
+ aria-valuemin="1"
210
+ aria-valuemax="3"
211
+ aria-valuenow="1.5"
212
+ aria-labelledby="labelLineHeight"
213
+ >
138
214
  </div>
139
215
 
140
- <div class="group" id="groupContrast">
216
+ <div class="group" id="groupContrast" role="group" aria-labelledby="labelContrast">
141
217
  <div class="label-row">
142
- <label for="contrast">Contrast</label>
143
- <span class="value" id="contrastValue"></span>
218
+ <label id="labelContrast" for="contrast">Contrast</label>
219
+ <span class="value" id="contrastValue" aria-hidden="true"></span>
144
220
  </div>
145
- <input type="range" id="contrast" min="50" max="200" value="100">
221
+
222
+ <input
223
+ type="range"
224
+ id="contrast"
225
+ min="50"
226
+ max="200"
227
+ value="100"
228
+ aria-valuemin="50"
229
+ aria-valuemax="200"
230
+ aria-valuenow="100"
231
+ aria-labelledby="labelContrast"
232
+ >
146
233
  </div>
147
234
 
148
- <div class="group" id="groupFontFamily">
235
+ <div class="group" id="groupFontFamily" role="group" aria-labelledby="labelFontFamily">
149
236
  <div class="label-row">
150
- <label for="fontFamily">Font family</label>
237
+ <label id="labelFontFamily" for="fontFamily">Font family</label>
151
238
  </div>
152
- <select id="fontFamily">
239
+
240
+ <select id="fontFamily" aria-labelledby="labelFontFamily">
153
241
  <option value="system-ui, sans-serif">System</option>
154
242
  <option value="Georgia, serif">Serif</option>
155
243
  <option value="'Courier New', monospace">Monospace</option>
156
244
  <option value="Verdana, sans-serif">Verdana</option>
245
+ <option value="inherit">Inherit</option>
157
246
  </select>
158
247
  </div>
159
248
  `;
160
249
  }
161
250
  connectedCallback() {
162
251
  this.targetSelector = this.getAttribute("target"), this.toggleGroup("#groupLetterSpacing", !this.hasAttribute("hide-letter-spacing")), this.toggleGroup("#groupWordSpacing", !this.hasAttribute("hide-word-spacing")), this.toggleGroup("#groupLineHeight", !this.hasAttribute("hide-line-height")), this.toggleGroup("#groupContrast", !this.hasAttribute("hide-contrast")), this.fontSize = this.shadowRoot.querySelector("#fontSize"), this.letterSpacing = this.shadowRoot.querySelector("#letterSpacing"), this.wordSpacing = this.shadowRoot.querySelector("#wordSpacing"), this.lineHeight = this.shadowRoot.querySelector("#lineHeight"), this.contrast = this.shadowRoot.querySelector("#contrast"), this.fontFamily = this.shadowRoot.querySelector("#fontFamily");
163
- const t = this.targetElement;
164
- if (t) {
165
- const i = getComputedStyle(t).fontFamily;
252
+ const e = this.targetElement;
253
+ if (e) {
254
+ const i = getComputedStyle(e).fontFamily;
166
255
  this.fontFamily.value = i;
167
256
  }
168
257
  this.fontSizeValue = this.shadowRoot.querySelector("#fontSizeValue"), this.letterSpacingValue = this.shadowRoot.querySelector("#letterSpacingValue"), this.wordSpacingValue = this.shadowRoot.querySelector("#wordSpacingValue"), this.lineHeightValue = this.shadowRoot.querySelector("#lineHeightValue"), this.contrastValue = this.shadowRoot.querySelector("#contrastValue");
169
- const e = () => {
258
+ const t = () => {
170
259
  this.update(), this.dispatchEvent(
171
260
  new CustomEvent("change", {
172
261
  detail: this.getValues(),
@@ -175,18 +264,18 @@ let n = class extends HTMLElement {
175
264
  })
176
265
  );
177
266
  };
178
- this.fontSize.addEventListener("input", e), this.letterSpacing.addEventListener("input", e), this.wordSpacing.addEventListener("input", e), this.lineHeight.addEventListener("input", e), this.contrast.addEventListener("input", e), this.fontFamily.addEventListener("change", e), this.update();
267
+ this.fontSize.addEventListener("input", t), this.letterSpacing.addEventListener("input", t), this.wordSpacing.addEventListener("input", t), this.lineHeight.addEventListener("input", t), this.contrast.addEventListener("input", t), this.fontFamily.addEventListener("change", t), this.update();
179
268
  }
180
- toggleGroup(t, e) {
181
- const i = this.shadowRoot.querySelector(t);
182
- i && (i.style.display = e ? "block" : "none");
269
+ toggleGroup(e, t) {
270
+ const i = this.shadowRoot.querySelector(e);
271
+ i && (i.style.display = t ? "block" : "none");
183
272
  }
184
273
  get targetElement() {
185
274
  return document.querySelector(this.targetSelector);
186
275
  }
187
276
  update() {
188
- const t = this.targetElement;
189
- t && (t.style.fontSize = `${this.fontSize.value}px`, t.style.letterSpacing = `${this.letterSpacing.value}px`, t.style.wordSpacing = `${this.wordSpacing.value}px`, t.style.lineHeight = this.lineHeight.value, t.style.filter = `contrast(${this.contrast.value}%)`, t.style.fontFamily = this.fontFamily.value, this.fontSizeValue.textContent = `${this.fontSize.value}px`, this.letterSpacingValue.textContent = `${this.letterSpacing.value}px`, this.wordSpacingValue.textContent = `${this.wordSpacing.value}px`, this.lineHeightValue.textContent = this.lineHeight.value, this.contrastValue.textContent = `${this.contrast.value}%`);
277
+ const e = this.targetElement;
278
+ e && (e.style.fontSize = `${this.fontSize.value}px`, e.style.letterSpacing = `${this.letterSpacing.value}px`, e.style.wordSpacing = `${this.wordSpacing.value}px`, e.style.lineHeight = this.lineHeight.value, e.style.filter = `contrast(${this.contrast.value}%)`, e.style.fontFamily = this.fontFamily.value, this.fontSizeValue.textContent = `${this.fontSize.value}px`, this.letterSpacingValue.textContent = `${this.letterSpacing.value}px`, this.wordSpacingValue.textContent = `${this.wordSpacing.value}px`, this.lineHeightValue.textContent = this.lineHeight.value, this.contrastValue.textContent = `${this.contrast.value}%`);
190
279
  }
191
280
  getValues() {
192
281
  return {
@@ -198,16 +287,22 @@ let n = class extends HTMLElement {
198
287
  fontFamily: this.fontFamily.value
199
288
  };
200
289
  }
201
- setValues(t = {}) {
202
- t.fontSize !== void 0 && (this.fontSize.value = t.fontSize), t.letterSpacing !== void 0 && (this.letterSpacing.value = t.letterSpacing), t.wordSpacing !== void 0 && (this.wordSpacing.value = t.wordSpacing), t.lineHeight !== void 0 && (this.lineHeight.value = t.lineHeight), t.contrast !== void 0 && (this.contrast.value = t.contrast), t.fontFamily !== void 0 && (this.fontFamily.value = t.fontFamily), this.update();
290
+ setValues(e = {}) {
291
+ if (e.fontSize !== void 0 && (this.fontSize.value = e.fontSize), e.letterSpacing !== void 0 && (this.letterSpacing.value = e.letterSpacing), e.wordSpacing !== void 0 && (this.wordSpacing.value = e.wordSpacing), e.lineHeight !== void 0 && (this.lineHeight.value = e.lineHeight), e.contrast !== void 0 && (this.contrast.value = e.contrast), e.fontFamily !== void 0)
292
+ if (e.fontFamily === "inherit" && target) {
293
+ const t = getComputedStyle(target).fontFamily;
294
+ this.fontFamily.value = t;
295
+ } else
296
+ this.fontFamily.value = e.fontFamily;
297
+ this.update();
203
298
  }
204
- setFeatures(t = {}) {
205
- t.letterSpacing !== void 0 && (this.toggleGroup("#groupLetterSpacing", t.letterSpacing), t.letterSpacing ? this.removeAttribute("hide-letter-spacing") : this.setAttribute("hide-letter-spacing", "")), t.wordSpacing !== void 0 && (this.toggleGroup("#groupWordSpacing", t.wordSpacing), t.wordSpacing ? this.removeAttribute("hide-word-spacing") : this.setAttribute("hide-word-spacing", "")), t.lineHeight !== void 0 && (this.toggleGroup("#groupLineHeight", t.lineHeight), t.lineHeight ? this.removeAttribute("hide-line-height") : this.setAttribute("hide-line-height", "")), t.contrast !== void 0 && (this.toggleGroup("#groupContrast", t.contrast), t.contrast ? this.removeAttribute("hide-contrast") : this.setAttribute("hide-contrast", ""));
299
+ setFeatures(e = {}) {
300
+ e.letterSpacing !== void 0 && (this.toggleGroup("#groupLetterSpacing", e.letterSpacing), e.letterSpacing ? this.removeAttribute("hide-letter-spacing") : this.setAttribute("hide-letter-spacing", "")), e.wordSpacing !== void 0 && (this.toggleGroup("#groupWordSpacing", e.wordSpacing), e.wordSpacing ? this.removeAttribute("hide-word-spacing") : this.setAttribute("hide-word-spacing", "")), e.lineHeight !== void 0 && (this.toggleGroup("#groupLineHeight", e.lineHeight), e.lineHeight ? this.removeAttribute("hide-line-height") : this.setAttribute("hide-line-height", "")), e.contrast !== void 0 && (this.toggleGroup("#groupContrast", e.contrast), e.contrast ? this.removeAttribute("hide-contrast") : this.setAttribute("hide-contrast", ""));
206
301
  }
207
302
  };
208
- customElements.define("typography-controller", n);
209
- const d = o({
210
- react: a,
303
+ customElements.define("typography-controller", r);
304
+ const p = a({
305
+ react: o,
211
306
  tagName: "typography-controller",
212
307
  elementClass: customElements.get("typography-controller"),
213
308
  events: {
@@ -215,5 +310,5 @@ const d = o({
215
310
  }
216
311
  });
217
312
  export {
218
- d as TypographyController
313
+ p as TypographyController
219
314
  };
@@ -1,4 +1,4 @@
1
- class o extends HTMLElement {
1
+ class a extends HTMLElement {
2
2
  constructor() {
3
3
  super(), this.attachShadow({ mode: "open" }), this.shadowRoot.innerHTML = `
4
4
  <style>
@@ -93,78 +93,167 @@ class o extends HTMLElement {
93
93
  }
94
94
 
95
95
  select:focus {
96
+ outline: none;
97
+ box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.35);
98
+ border-radius: 999px;
99
+ }
100
+
101
+ /* Highlight the track when focused */
102
+ input[type="range"]:focus {
103
+ outline: none;
104
+ box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.35);
105
+ border-radius: 999px;
106
+ }
107
+
108
+ /* Glow effect on the thumb when focused */
109
+ input[type="range"]:focus::-webkit-slider-thumb {
110
+ box-shadow:
111
+ 0 0 0 4px rgba(37, 99, 235, 0.35),
112
+ 0 4px 10px rgba(0, 0, 0, 0.25);
113
+ border-color: #2563eb;
114
+ }
115
+
116
+ /* Firefox thumb focus */
117
+ input[type="range"]::-moz-range-thumb:focus {
118
+ box-shadow:
119
+ 0 0 0 4px rgba(37, 99, 235, 0.35),
120
+ 0 4px 10px rgba(0, 0, 0, 0.25);
96
121
  border-color: #2563eb;
97
- box-shadow: 0 0 0 1px rgba(37, 99, 235, 0.4);
122
+ }
123
+ input[type="range"]:hover::-webkit-slider-thumb {
124
+ transform: scale(1.05);
125
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
98
126
  }
99
127
  </style>
100
128
 
101
129
  <div class="header">
102
- <div class="title">Typography Controls</div>
103
- <div class="badge">Web Component</div>
130
+ <h2 class="title" id="controllerTitle">Typography Controls</h2>
131
+ <span class="badge">v1.0.0</span>
104
132
  </div>
105
133
 
106
- <div class="group" id="groupFontSize">
134
+ <!-- Live region for announcements -->
135
+ <div aria-live="polite" class="sr-only" id="liveRegion"></div>
136
+
137
+ <div class="group" id="groupFontSize" role="group" aria-labelledby="labelFontSize">
107
138
  <div class="label-row">
108
- <label for="fontSize">Font size</label>
109
- <span class="value" id="fontSizeValue"></span>
139
+ <label id="labelFontSize" for="fontSize">Font size</label>
140
+ <span class="value" id="fontSizeValue" aria-hidden="true"></span>
110
141
  </div>
111
- <input type="range" id="fontSize" min="12" max="60" value="24">
142
+
143
+ <input
144
+ type="range"
145
+ id="fontSize"
146
+ min="12"
147
+ max="60"
148
+ value="24"
149
+ aria-valuemin="12"
150
+ aria-valuemax="60"
151
+ aria-valuenow="24"
152
+ aria-labelledby="labelFontSize"
153
+ >
112
154
  </div>
113
155
 
114
- <div class="group" id="groupLetterSpacing">
156
+ <div class="group" id="groupLetterSpacing" role="group" aria-labelledby="labelLetterSpacing">
115
157
  <div class="label-row">
116
- <label for="letterSpacing">Letter spacing</label>
117
- <span class="value" id="letterSpacingValue"></span>
158
+ <label id="labelLetterSpacing" for="letterSpacing">Letter spacing</label>
159
+ <span class="value" id="letterSpacingValue" aria-hidden="true"></span>
118
160
  </div>
119
- <input type="range" id="letterSpacing" min="0" max="20" value="0">
161
+
162
+ <input
163
+ type="range"
164
+ id="letterSpacing"
165
+ min="0"
166
+ max="20"
167
+ value="0"
168
+ aria-valuemin="0"
169
+ aria-valuemax="20"
170
+ aria-valuenow="0"
171
+ aria-labelledby="labelLetterSpacing"
172
+ >
120
173
  </div>
121
174
 
122
- <div class="group" id="groupWordSpacing">
175
+ <div class="group" id="groupWordSpacing" role="group" aria-labelledby="labelWordSpacing">
123
176
  <div class="label-row">
124
- <label for="wordSpacing">Word spacing</label>
125
- <span class="value" id="wordSpacingValue"></span>
177
+ <label id="labelWordSpacing" for="wordSpacing">Word spacing</label>
178
+ <span class="value" id="wordSpacingValue" aria-hidden="true"></span>
126
179
  </div>
127
- <input type="range" id="wordSpacing" min="0" max="40" value="0">
180
+
181
+ <input
182
+ type="range"
183
+ id="wordSpacing"
184
+ min="0"
185
+ max="40"
186
+ value="0"
187
+ aria-valuemin="0"
188
+ aria-valuemax="40"
189
+ aria-valuenow="0"
190
+ aria-labelledby="labelWordSpacing"
191
+ >
128
192
  </div>
129
193
 
130
- <div class="group" id="groupLineHeight">
194
+ <div class="group" id="groupLineHeight" role="group" aria-labelledby="labelLineHeight">
131
195
  <div class="label-row">
132
- <label for="lineHeight">Line height</label>
133
- <span class="value" id="lineHeightValue"></span>
196
+ <label id="labelLineHeight" for="lineHeight">Line height</label>
197
+ <span class="value" id="lineHeightValue" aria-hidden="true"></span>
134
198
  </div>
135
- <input type="range" id="lineHeight" min="1" max="3" step="0.1" value="1.5">
199
+
200
+ <input
201
+ type="range"
202
+ id="lineHeight"
203
+ min="1"
204
+ max="3"
205
+ step="0.1"
206
+ value="1.5"
207
+ aria-valuemin="1"
208
+ aria-valuemax="3"
209
+ aria-valuenow="1.5"
210
+ aria-labelledby="labelLineHeight"
211
+ >
136
212
  </div>
137
213
 
138
- <div class="group" id="groupContrast">
214
+ <div class="group" id="groupContrast" role="group" aria-labelledby="labelContrast">
139
215
  <div class="label-row">
140
- <label for="contrast">Contrast</label>
141
- <span class="value" id="contrastValue"></span>
216
+ <label id="labelContrast" for="contrast">Contrast</label>
217
+ <span class="value" id="contrastValue" aria-hidden="true"></span>
142
218
  </div>
143
- <input type="range" id="contrast" min="50" max="200" value="100">
219
+
220
+ <input
221
+ type="range"
222
+ id="contrast"
223
+ min="50"
224
+ max="200"
225
+ value="100"
226
+ aria-valuemin="50"
227
+ aria-valuemax="200"
228
+ aria-valuenow="100"
229
+ aria-labelledby="labelContrast"
230
+ >
144
231
  </div>
145
232
 
146
- <div class="group" id="groupFontFamily">
233
+ <div class="group" id="groupFontFamily" role="group" aria-labelledby="labelFontFamily">
147
234
  <div class="label-row">
148
- <label for="fontFamily">Font family</label>
235
+ <label id="labelFontFamily" for="fontFamily">Font family</label>
149
236
  </div>
150
- <select id="fontFamily">
237
+
238
+ <select id="fontFamily" aria-labelledby="labelFontFamily">
151
239
  <option value="system-ui, sans-serif">System</option>
152
240
  <option value="Georgia, serif">Serif</option>
153
241
  <option value="'Courier New', monospace">Monospace</option>
154
242
  <option value="Verdana, sans-serif">Verdana</option>
243
+ <option value="inherit">Inherit</option>
155
244
  </select>
156
245
  </div>
157
246
  `;
158
247
  }
159
248
  connectedCallback() {
160
249
  this.targetSelector = this.getAttribute("target"), this.toggleGroup("#groupLetterSpacing", !this.hasAttribute("hide-letter-spacing")), this.toggleGroup("#groupWordSpacing", !this.hasAttribute("hide-word-spacing")), this.toggleGroup("#groupLineHeight", !this.hasAttribute("hide-line-height")), this.toggleGroup("#groupContrast", !this.hasAttribute("hide-contrast")), this.fontSize = this.shadowRoot.querySelector("#fontSize"), this.letterSpacing = this.shadowRoot.querySelector("#letterSpacing"), this.wordSpacing = this.shadowRoot.querySelector("#wordSpacing"), this.lineHeight = this.shadowRoot.querySelector("#lineHeight"), this.contrast = this.shadowRoot.querySelector("#contrast"), this.fontFamily = this.shadowRoot.querySelector("#fontFamily");
161
- const t = this.targetElement;
162
- if (t) {
163
- const i = getComputedStyle(t).fontFamily;
250
+ const e = this.targetElement;
251
+ if (e) {
252
+ const i = getComputedStyle(e).fontFamily;
164
253
  this.fontFamily.value = i;
165
254
  }
166
255
  this.fontSizeValue = this.shadowRoot.querySelector("#fontSizeValue"), this.letterSpacingValue = this.shadowRoot.querySelector("#letterSpacingValue"), this.wordSpacingValue = this.shadowRoot.querySelector("#wordSpacingValue"), this.lineHeightValue = this.shadowRoot.querySelector("#lineHeightValue"), this.contrastValue = this.shadowRoot.querySelector("#contrastValue");
167
- const e = () => {
256
+ const t = () => {
168
257
  this.update(), this.dispatchEvent(
169
258
  new CustomEvent("change", {
170
259
  detail: this.getValues(),
@@ -173,18 +262,18 @@ class o extends HTMLElement {
173
262
  })
174
263
  );
175
264
  };
176
- this.fontSize.addEventListener("input", e), this.letterSpacing.addEventListener("input", e), this.wordSpacing.addEventListener("input", e), this.lineHeight.addEventListener("input", e), this.contrast.addEventListener("input", e), this.fontFamily.addEventListener("change", e), this.update();
265
+ this.fontSize.addEventListener("input", t), this.letterSpacing.addEventListener("input", t), this.wordSpacing.addEventListener("input", t), this.lineHeight.addEventListener("input", t), this.contrast.addEventListener("input", t), this.fontFamily.addEventListener("change", t), this.update();
177
266
  }
178
- toggleGroup(t, e) {
179
- const i = this.shadowRoot.querySelector(t);
180
- i && (i.style.display = e ? "block" : "none");
267
+ toggleGroup(e, t) {
268
+ const i = this.shadowRoot.querySelector(e);
269
+ i && (i.style.display = t ? "block" : "none");
181
270
  }
182
271
  get targetElement() {
183
272
  return document.querySelector(this.targetSelector);
184
273
  }
185
274
  update() {
186
- const t = this.targetElement;
187
- t && (t.style.fontSize = `${this.fontSize.value}px`, t.style.letterSpacing = `${this.letterSpacing.value}px`, t.style.wordSpacing = `${this.wordSpacing.value}px`, t.style.lineHeight = this.lineHeight.value, t.style.filter = `contrast(${this.contrast.value}%)`, t.style.fontFamily = this.fontFamily.value, this.fontSizeValue.textContent = `${this.fontSize.value}px`, this.letterSpacingValue.textContent = `${this.letterSpacing.value}px`, this.wordSpacingValue.textContent = `${this.wordSpacing.value}px`, this.lineHeightValue.textContent = this.lineHeight.value, this.contrastValue.textContent = `${this.contrast.value}%`);
275
+ const e = this.targetElement;
276
+ e && (e.style.fontSize = `${this.fontSize.value}px`, e.style.letterSpacing = `${this.letterSpacing.value}px`, e.style.wordSpacing = `${this.wordSpacing.value}px`, e.style.lineHeight = this.lineHeight.value, e.style.filter = `contrast(${this.contrast.value}%)`, e.style.fontFamily = this.fontFamily.value, this.fontSizeValue.textContent = `${this.fontSize.value}px`, this.letterSpacingValue.textContent = `${this.letterSpacing.value}px`, this.wordSpacingValue.textContent = `${this.wordSpacing.value}px`, this.lineHeightValue.textContent = this.lineHeight.value, this.contrastValue.textContent = `${this.contrast.value}%`);
188
277
  }
189
278
  getValues() {
190
279
  return {
@@ -196,11 +285,17 @@ class o extends HTMLElement {
196
285
  fontFamily: this.fontFamily.value
197
286
  };
198
287
  }
199
- setValues(t = {}) {
200
- t.fontSize !== void 0 && (this.fontSize.value = t.fontSize), t.letterSpacing !== void 0 && (this.letterSpacing.value = t.letterSpacing), t.wordSpacing !== void 0 && (this.wordSpacing.value = t.wordSpacing), t.lineHeight !== void 0 && (this.lineHeight.value = t.lineHeight), t.contrast !== void 0 && (this.contrast.value = t.contrast), t.fontFamily !== void 0 && (this.fontFamily.value = t.fontFamily), this.update();
288
+ setValues(e = {}) {
289
+ if (e.fontSize !== void 0 && (this.fontSize.value = e.fontSize), e.letterSpacing !== void 0 && (this.letterSpacing.value = e.letterSpacing), e.wordSpacing !== void 0 && (this.wordSpacing.value = e.wordSpacing), e.lineHeight !== void 0 && (this.lineHeight.value = e.lineHeight), e.contrast !== void 0 && (this.contrast.value = e.contrast), e.fontFamily !== void 0)
290
+ if (e.fontFamily === "inherit" && target) {
291
+ const t = getComputedStyle(target).fontFamily;
292
+ this.fontFamily.value = t;
293
+ } else
294
+ this.fontFamily.value = e.fontFamily;
295
+ this.update();
201
296
  }
202
- setFeatures(t = {}) {
203
- t.letterSpacing !== void 0 && (this.toggleGroup("#groupLetterSpacing", t.letterSpacing), t.letterSpacing ? this.removeAttribute("hide-letter-spacing") : this.setAttribute("hide-letter-spacing", "")), t.wordSpacing !== void 0 && (this.toggleGroup("#groupWordSpacing", t.wordSpacing), t.wordSpacing ? this.removeAttribute("hide-word-spacing") : this.setAttribute("hide-word-spacing", "")), t.lineHeight !== void 0 && (this.toggleGroup("#groupLineHeight", t.lineHeight), t.lineHeight ? this.removeAttribute("hide-line-height") : this.setAttribute("hide-line-height", "")), t.contrast !== void 0 && (this.toggleGroup("#groupContrast", t.contrast), t.contrast ? this.removeAttribute("hide-contrast") : this.setAttribute("hide-contrast", ""));
297
+ setFeatures(e = {}) {
298
+ e.letterSpacing !== void 0 && (this.toggleGroup("#groupLetterSpacing", e.letterSpacing), e.letterSpacing ? this.removeAttribute("hide-letter-spacing") : this.setAttribute("hide-letter-spacing", "")), e.wordSpacing !== void 0 && (this.toggleGroup("#groupWordSpacing", e.wordSpacing), e.wordSpacing ? this.removeAttribute("hide-word-spacing") : this.setAttribute("hide-word-spacing", "")), e.lineHeight !== void 0 && (this.toggleGroup("#groupLineHeight", e.lineHeight), e.lineHeight ? this.removeAttribute("hide-line-height") : this.setAttribute("hide-line-height", "")), e.contrast !== void 0 && (this.toggleGroup("#groupContrast", e.contrast), e.contrast ? this.removeAttribute("hide-contrast") : this.setAttribute("hide-contrast", ""));
204
299
  }
205
300
  }
206
- customElements.define("typography-controller", o);
301
+ customElements.define("typography-controller", a);
@@ -1,4 +1,4 @@
1
- (function(i){typeof define=="function"&&define.amd?define(i):i()})(function(){"use strict";class i extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}),this.shadowRoot.innerHTML=`
1
+ (function(i){typeof define=="function"&&define.amd?define(i):i()})((function(){"use strict";class i extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}),this.shadowRoot.innerHTML=`
2
2
  <style>
3
3
  :host {
4
4
  display: block;
@@ -91,65 +91,154 @@
91
91
  }
92
92
 
93
93
  select:focus {
94
+ outline: none;
95
+ box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.35);
96
+ border-radius: 999px;
97
+ }
98
+
99
+ /* Highlight the track when focused */
100
+ input[type="range"]:focus {
101
+ outline: none;
102
+ box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.35);
103
+ border-radius: 999px;
104
+ }
105
+
106
+ /* Glow effect on the thumb when focused */
107
+ input[type="range"]:focus::-webkit-slider-thumb {
108
+ box-shadow:
109
+ 0 0 0 4px rgba(37, 99, 235, 0.35),
110
+ 0 4px 10px rgba(0, 0, 0, 0.25);
111
+ border-color: #2563eb;
112
+ }
113
+
114
+ /* Firefox thumb focus */
115
+ input[type="range"]::-moz-range-thumb:focus {
116
+ box-shadow:
117
+ 0 0 0 4px rgba(37, 99, 235, 0.35),
118
+ 0 4px 10px rgba(0, 0, 0, 0.25);
94
119
  border-color: #2563eb;
95
- box-shadow: 0 0 0 1px rgba(37, 99, 235, 0.4);
120
+ }
121
+ input[type="range"]:hover::-webkit-slider-thumb {
122
+ transform: scale(1.05);
123
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
96
124
  }
97
125
  </style>
98
126
 
99
127
  <div class="header">
100
- <div class="title">Typography Controls</div>
101
- <div class="badge">Web Component</div>
128
+ <h2 class="title" id="controllerTitle">Typography Controls</h2>
129
+ <span class="badge">v1.0.0</span>
102
130
  </div>
103
131
 
104
- <div class="group" id="groupFontSize">
132
+ <!-- Live region for announcements -->
133
+ <div aria-live="polite" class="sr-only" id="liveRegion"></div>
134
+
135
+ <div class="group" id="groupFontSize" role="group" aria-labelledby="labelFontSize">
105
136
  <div class="label-row">
106
- <label for="fontSize">Font size</label>
107
- <span class="value" id="fontSizeValue"></span>
137
+ <label id="labelFontSize" for="fontSize">Font size</label>
138
+ <span class="value" id="fontSizeValue" aria-hidden="true"></span>
108
139
  </div>
109
- <input type="range" id="fontSize" min="12" max="60" value="24">
140
+
141
+ <input
142
+ type="range"
143
+ id="fontSize"
144
+ min="12"
145
+ max="60"
146
+ value="24"
147
+ aria-valuemin="12"
148
+ aria-valuemax="60"
149
+ aria-valuenow="24"
150
+ aria-labelledby="labelFontSize"
151
+ >
110
152
  </div>
111
153
 
112
- <div class="group" id="groupLetterSpacing">
154
+ <div class="group" id="groupLetterSpacing" role="group" aria-labelledby="labelLetterSpacing">
113
155
  <div class="label-row">
114
- <label for="letterSpacing">Letter spacing</label>
115
- <span class="value" id="letterSpacingValue"></span>
156
+ <label id="labelLetterSpacing" for="letterSpacing">Letter spacing</label>
157
+ <span class="value" id="letterSpacingValue" aria-hidden="true"></span>
116
158
  </div>
117
- <input type="range" id="letterSpacing" min="0" max="20" value="0">
159
+
160
+ <input
161
+ type="range"
162
+ id="letterSpacing"
163
+ min="0"
164
+ max="20"
165
+ value="0"
166
+ aria-valuemin="0"
167
+ aria-valuemax="20"
168
+ aria-valuenow="0"
169
+ aria-labelledby="labelLetterSpacing"
170
+ >
118
171
  </div>
119
172
 
120
- <div class="group" id="groupWordSpacing">
173
+ <div class="group" id="groupWordSpacing" role="group" aria-labelledby="labelWordSpacing">
121
174
  <div class="label-row">
122
- <label for="wordSpacing">Word spacing</label>
123
- <span class="value" id="wordSpacingValue"></span>
175
+ <label id="labelWordSpacing" for="wordSpacing">Word spacing</label>
176
+ <span class="value" id="wordSpacingValue" aria-hidden="true"></span>
124
177
  </div>
125
- <input type="range" id="wordSpacing" min="0" max="40" value="0">
178
+
179
+ <input
180
+ type="range"
181
+ id="wordSpacing"
182
+ min="0"
183
+ max="40"
184
+ value="0"
185
+ aria-valuemin="0"
186
+ aria-valuemax="40"
187
+ aria-valuenow="0"
188
+ aria-labelledby="labelWordSpacing"
189
+ >
126
190
  </div>
127
191
 
128
- <div class="group" id="groupLineHeight">
192
+ <div class="group" id="groupLineHeight" role="group" aria-labelledby="labelLineHeight">
129
193
  <div class="label-row">
130
- <label for="lineHeight">Line height</label>
131
- <span class="value" id="lineHeightValue"></span>
194
+ <label id="labelLineHeight" for="lineHeight">Line height</label>
195
+ <span class="value" id="lineHeightValue" aria-hidden="true"></span>
132
196
  </div>
133
- <input type="range" id="lineHeight" min="1" max="3" step="0.1" value="1.5">
197
+
198
+ <input
199
+ type="range"
200
+ id="lineHeight"
201
+ min="1"
202
+ max="3"
203
+ step="0.1"
204
+ value="1.5"
205
+ aria-valuemin="1"
206
+ aria-valuemax="3"
207
+ aria-valuenow="1.5"
208
+ aria-labelledby="labelLineHeight"
209
+ >
134
210
  </div>
135
211
 
136
- <div class="group" id="groupContrast">
212
+ <div class="group" id="groupContrast" role="group" aria-labelledby="labelContrast">
137
213
  <div class="label-row">
138
- <label for="contrast">Contrast</label>
139
- <span class="value" id="contrastValue"></span>
214
+ <label id="labelContrast" for="contrast">Contrast</label>
215
+ <span class="value" id="contrastValue" aria-hidden="true"></span>
140
216
  </div>
141
- <input type="range" id="contrast" min="50" max="200" value="100">
217
+
218
+ <input
219
+ type="range"
220
+ id="contrast"
221
+ min="50"
222
+ max="200"
223
+ value="100"
224
+ aria-valuemin="50"
225
+ aria-valuemax="200"
226
+ aria-valuenow="100"
227
+ aria-labelledby="labelContrast"
228
+ >
142
229
  </div>
143
230
 
144
- <div class="group" id="groupFontFamily">
231
+ <div class="group" id="groupFontFamily" role="group" aria-labelledby="labelFontFamily">
145
232
  <div class="label-row">
146
- <label for="fontFamily">Font family</label>
233
+ <label id="labelFontFamily" for="fontFamily">Font family</label>
147
234
  </div>
148
- <select id="fontFamily">
235
+
236
+ <select id="fontFamily" aria-labelledby="labelFontFamily">
149
237
  <option value="system-ui, sans-serif">System</option>
150
238
  <option value="Georgia, serif">Serif</option>
151
239
  <option value="'Courier New', monospace">Monospace</option>
152
240
  <option value="Verdana, sans-serif">Verdana</option>
241
+ <option value="inherit">Inherit</option>
153
242
  </select>
154
243
  </div>
155
- `}connectedCallback(){this.targetSelector=this.getAttribute("target"),this.toggleGroup("#groupLetterSpacing",!this.hasAttribute("hide-letter-spacing")),this.toggleGroup("#groupWordSpacing",!this.hasAttribute("hide-word-spacing")),this.toggleGroup("#groupLineHeight",!this.hasAttribute("hide-line-height")),this.toggleGroup("#groupContrast",!this.hasAttribute("hide-contrast")),this.fontSize=this.shadowRoot.querySelector("#fontSize"),this.letterSpacing=this.shadowRoot.querySelector("#letterSpacing"),this.wordSpacing=this.shadowRoot.querySelector("#wordSpacing"),this.lineHeight=this.shadowRoot.querySelector("#lineHeight"),this.contrast=this.shadowRoot.querySelector("#contrast"),this.fontFamily=this.shadowRoot.querySelector("#fontFamily");const t=this.targetElement;if(t){const o=getComputedStyle(t).fontFamily;this.fontFamily.value=o}this.fontSizeValue=this.shadowRoot.querySelector("#fontSizeValue"),this.letterSpacingValue=this.shadowRoot.querySelector("#letterSpacingValue"),this.wordSpacingValue=this.shadowRoot.querySelector("#wordSpacingValue"),this.lineHeightValue=this.shadowRoot.querySelector("#lineHeightValue"),this.contrastValue=this.shadowRoot.querySelector("#contrastValue");const e=()=>{this.update(),this.dispatchEvent(new CustomEvent("change",{detail:this.getValues(),bubbles:!0,composed:!0}))};this.fontSize.addEventListener("input",e),this.letterSpacing.addEventListener("input",e),this.wordSpacing.addEventListener("input",e),this.lineHeight.addEventListener("input",e),this.contrast.addEventListener("input",e),this.fontFamily.addEventListener("change",e),this.update()}toggleGroup(t,e){const o=this.shadowRoot.querySelector(t);o&&(o.style.display=e?"block":"none")}get targetElement(){return document.querySelector(this.targetSelector)}update(){const t=this.targetElement;t&&(t.style.fontSize=`${this.fontSize.value}px`,t.style.letterSpacing=`${this.letterSpacing.value}px`,t.style.wordSpacing=`${this.wordSpacing.value}px`,t.style.lineHeight=this.lineHeight.value,t.style.filter=`contrast(${this.contrast.value}%)`,t.style.fontFamily=this.fontFamily.value,this.fontSizeValue.textContent=`${this.fontSize.value}px`,this.letterSpacingValue.textContent=`${this.letterSpacing.value}px`,this.wordSpacingValue.textContent=`${this.wordSpacing.value}px`,this.lineHeightValue.textContent=this.lineHeight.value,this.contrastValue.textContent=`${this.contrast.value}%`)}getValues(){return{fontSize:Number(this.fontSize.value),letterSpacing:Number(this.letterSpacing.value),wordSpacing:Number(this.wordSpacing.value),lineHeight:Number(this.lineHeight.value),contrast:Number(this.contrast.value),fontFamily:this.fontFamily.value}}setValues(t={}){t.fontSize!==void 0&&(this.fontSize.value=t.fontSize),t.letterSpacing!==void 0&&(this.letterSpacing.value=t.letterSpacing),t.wordSpacing!==void 0&&(this.wordSpacing.value=t.wordSpacing),t.lineHeight!==void 0&&(this.lineHeight.value=t.lineHeight),t.contrast!==void 0&&(this.contrast.value=t.contrast),t.fontFamily!==void 0&&(this.fontFamily.value=t.fontFamily),this.update()}setFeatures(t={}){t.letterSpacing!==void 0&&(this.toggleGroup("#groupLetterSpacing",t.letterSpacing),t.letterSpacing?this.removeAttribute("hide-letter-spacing"):this.setAttribute("hide-letter-spacing","")),t.wordSpacing!==void 0&&(this.toggleGroup("#groupWordSpacing",t.wordSpacing),t.wordSpacing?this.removeAttribute("hide-word-spacing"):this.setAttribute("hide-word-spacing","")),t.lineHeight!==void 0&&(this.toggleGroup("#groupLineHeight",t.lineHeight),t.lineHeight?this.removeAttribute("hide-line-height"):this.setAttribute("hide-line-height","")),t.contrast!==void 0&&(this.toggleGroup("#groupContrast",t.contrast),t.contrast?this.removeAttribute("hide-contrast"):this.setAttribute("hide-contrast",""))}}customElements.define("typography-controller",i)});
244
+ `}connectedCallback(){this.targetSelector=this.getAttribute("target"),this.toggleGroup("#groupLetterSpacing",!this.hasAttribute("hide-letter-spacing")),this.toggleGroup("#groupWordSpacing",!this.hasAttribute("hide-word-spacing")),this.toggleGroup("#groupLineHeight",!this.hasAttribute("hide-line-height")),this.toggleGroup("#groupContrast",!this.hasAttribute("hide-contrast")),this.fontSize=this.shadowRoot.querySelector("#fontSize"),this.letterSpacing=this.shadowRoot.querySelector("#letterSpacing"),this.wordSpacing=this.shadowRoot.querySelector("#wordSpacing"),this.lineHeight=this.shadowRoot.querySelector("#lineHeight"),this.contrast=this.shadowRoot.querySelector("#contrast"),this.fontFamily=this.shadowRoot.querySelector("#fontFamily");const e=this.targetElement;if(e){const a=getComputedStyle(e).fontFamily;this.fontFamily.value=a}this.fontSizeValue=this.shadowRoot.querySelector("#fontSizeValue"),this.letterSpacingValue=this.shadowRoot.querySelector("#letterSpacingValue"),this.wordSpacingValue=this.shadowRoot.querySelector("#wordSpacingValue"),this.lineHeightValue=this.shadowRoot.querySelector("#lineHeightValue"),this.contrastValue=this.shadowRoot.querySelector("#contrastValue");const t=()=>{this.update(),this.dispatchEvent(new CustomEvent("change",{detail:this.getValues(),bubbles:!0,composed:!0}))};this.fontSize.addEventListener("input",t),this.letterSpacing.addEventListener("input",t),this.wordSpacing.addEventListener("input",t),this.lineHeight.addEventListener("input",t),this.contrast.addEventListener("input",t),this.fontFamily.addEventListener("change",t),this.update()}toggleGroup(e,t){const a=this.shadowRoot.querySelector(e);a&&(a.style.display=t?"block":"none")}get targetElement(){return document.querySelector(this.targetSelector)}update(){const e=this.targetElement;e&&(e.style.fontSize=`${this.fontSize.value}px`,e.style.letterSpacing=`${this.letterSpacing.value}px`,e.style.wordSpacing=`${this.wordSpacing.value}px`,e.style.lineHeight=this.lineHeight.value,e.style.filter=`contrast(${this.contrast.value}%)`,e.style.fontFamily=this.fontFamily.value,this.fontSizeValue.textContent=`${this.fontSize.value}px`,this.letterSpacingValue.textContent=`${this.letterSpacing.value}px`,this.wordSpacingValue.textContent=`${this.wordSpacing.value}px`,this.lineHeightValue.textContent=this.lineHeight.value,this.contrastValue.textContent=`${this.contrast.value}%`)}getValues(){return{fontSize:Number(this.fontSize.value),letterSpacing:Number(this.letterSpacing.value),wordSpacing:Number(this.wordSpacing.value),lineHeight:Number(this.lineHeight.value),contrast:Number(this.contrast.value),fontFamily:this.fontFamily.value}}setValues(e={}){if(e.fontSize!==void 0&&(this.fontSize.value=e.fontSize),e.letterSpacing!==void 0&&(this.letterSpacing.value=e.letterSpacing),e.wordSpacing!==void 0&&(this.wordSpacing.value=e.wordSpacing),e.lineHeight!==void 0&&(this.lineHeight.value=e.lineHeight),e.contrast!==void 0&&(this.contrast.value=e.contrast),e.fontFamily!==void 0)if(e.fontFamily==="inherit"&&target){const t=getComputedStyle(target).fontFamily;this.fontFamily.value=t}else this.fontFamily.value=e.fontFamily;this.update()}setFeatures(e={}){e.letterSpacing!==void 0&&(this.toggleGroup("#groupLetterSpacing",e.letterSpacing),e.letterSpacing?this.removeAttribute("hide-letter-spacing"):this.setAttribute("hide-letter-spacing","")),e.wordSpacing!==void 0&&(this.toggleGroup("#groupWordSpacing",e.wordSpacing),e.wordSpacing?this.removeAttribute("hide-word-spacing"):this.setAttribute("hide-word-spacing","")),e.lineHeight!==void 0&&(this.toggleGroup("#groupLineHeight",e.lineHeight),e.lineHeight?this.removeAttribute("hide-line-height"):this.setAttribute("hide-line-height","")),e.contrast!==void 0&&(this.toggleGroup("#groupContrast",e.contrast),e.contrast?this.removeAttribute("hide-contrast"):this.setAttribute("hide-contrast",""))}}customElements.define("typography-controller",i)}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "typography-controller",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
4
4
  "description": "A framework-agnostic Web Component that provides a beautiful typography control panel with sliders for font size, letter spacing, word spacing, line height, contrast, and font family.",
5
5
  "author": "Achuth Kamath",
6
6
  "license": "MIT",
@@ -44,14 +44,14 @@
44
44
  },
45
45
  "devDependencies": {
46
46
  "@semantic-release/changelog": "^6.0.3",
47
- "@semantic-release/commit-analyzer": "^13.0.0",
47
+ "@semantic-release/commit-analyzer": "^13.0.1",
48
48
  "@semantic-release/git": "^10.0.1",
49
49
  "@semantic-release/github": "^12.0.3",
50
50
  "@semantic-release/npm": "^13.1.3",
51
- "@semantic-release/release-notes-generator": "^14.0.1",
51
+ "@semantic-release/release-notes-generator": "^14.1.0",
52
52
  "@vitejs/plugin-react": "^5.1.2",
53
53
  "semantic-release": "^25.0.3",
54
- "vite": "^5.4.21"
54
+ "vite": "^7.3.1"
55
55
  },
56
56
  "dependencies": {
57
57
  "user": "^0.0.0"
@@ -0,0 +1,22 @@
1
+ export class TypographyState {
2
+ constructor(targetElement) {
3
+ this.target = targetElement;
4
+ }
5
+
6
+ resolveFontFamily(value) {
7
+ if (value === "inherit" && this.target) {
8
+ return getComputedStyle(this.target).fontFamily;
9
+ }
10
+ return value;
11
+ }
12
+
13
+ apply(values = {}) {
14
+ const resolved = { ...values };
15
+
16
+ if (values.fontFamily !== undefined) {
17
+ resolved.fontFamily = this.resolveFontFamily(values.fontFamily);
18
+ }
19
+
20
+ return resolved;
21
+ }
22
+ }
@@ -1,3 +1,5 @@
1
+ import { TypographyState } from "./TypographyState.js";
2
+
1
3
  class TypographyController extends HTMLElement {
2
4
  constructor() {
3
5
  super();
@@ -96,65 +98,154 @@ class TypographyController extends HTMLElement {
96
98
  }
97
99
 
98
100
  select:focus {
101
+ outline: none;
102
+ box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.35);
103
+ border-radius: 999px;
104
+ }
105
+
106
+ /* Highlight the track when focused */
107
+ input[type="range"]:focus {
108
+ outline: none;
109
+ box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.35);
110
+ border-radius: 999px;
111
+ }
112
+
113
+ /* Glow effect on the thumb when focused */
114
+ input[type="range"]:focus::-webkit-slider-thumb {
115
+ box-shadow:
116
+ 0 0 0 4px rgba(37, 99, 235, 0.35),
117
+ 0 4px 10px rgba(0, 0, 0, 0.25);
118
+ border-color: #2563eb;
119
+ }
120
+
121
+ /* Firefox thumb focus */
122
+ input[type="range"]::-moz-range-thumb:focus {
123
+ box-shadow:
124
+ 0 0 0 4px rgba(37, 99, 235, 0.35),
125
+ 0 4px 10px rgba(0, 0, 0, 0.25);
99
126
  border-color: #2563eb;
100
- box-shadow: 0 0 0 1px rgba(37, 99, 235, 0.4);
127
+ }
128
+ input[type="range"]:hover::-webkit-slider-thumb {
129
+ transform: scale(1.05);
130
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
101
131
  }
102
132
  </style>
103
133
 
104
134
  <div class="header">
105
- <div class="title">Typography Controls</div>
106
- <div class="badge">Web Component</div>
135
+ <h2 class="title" id="controllerTitle">Typography Controls</h2>
136
+ <span class="badge">v1.0.0</span>
107
137
  </div>
108
138
 
109
- <div class="group" id="groupFontSize">
139
+ <!-- Live region for announcements -->
140
+ <div aria-live="polite" class="sr-only" id="liveRegion"></div>
141
+
142
+ <div class="group" id="groupFontSize" role="group" aria-labelledby="labelFontSize">
110
143
  <div class="label-row">
111
- <label for="fontSize">Font size</label>
112
- <span class="value" id="fontSizeValue"></span>
144
+ <label id="labelFontSize" for="fontSize">Font size</label>
145
+ <span class="value" id="fontSizeValue" aria-hidden="true"></span>
113
146
  </div>
114
- <input type="range" id="fontSize" min="12" max="60" value="24">
147
+
148
+ <input
149
+ type="range"
150
+ id="fontSize"
151
+ min="12"
152
+ max="60"
153
+ value="24"
154
+ aria-valuemin="12"
155
+ aria-valuemax="60"
156
+ aria-valuenow="24"
157
+ aria-labelledby="labelFontSize"
158
+ >
115
159
  </div>
116
160
 
117
- <div class="group" id="groupLetterSpacing">
161
+ <div class="group" id="groupLetterSpacing" role="group" aria-labelledby="labelLetterSpacing">
118
162
  <div class="label-row">
119
- <label for="letterSpacing">Letter spacing</label>
120
- <span class="value" id="letterSpacingValue"></span>
163
+ <label id="labelLetterSpacing" for="letterSpacing">Letter spacing</label>
164
+ <span class="value" id="letterSpacingValue" aria-hidden="true"></span>
121
165
  </div>
122
- <input type="range" id="letterSpacing" min="0" max="20" value="0">
166
+
167
+ <input
168
+ type="range"
169
+ id="letterSpacing"
170
+ min="0"
171
+ max="20"
172
+ value="0"
173
+ aria-valuemin="0"
174
+ aria-valuemax="20"
175
+ aria-valuenow="0"
176
+ aria-labelledby="labelLetterSpacing"
177
+ >
123
178
  </div>
124
179
 
125
- <div class="group" id="groupWordSpacing">
180
+ <div class="group" id="groupWordSpacing" role="group" aria-labelledby="labelWordSpacing">
126
181
  <div class="label-row">
127
- <label for="wordSpacing">Word spacing</label>
128
- <span class="value" id="wordSpacingValue"></span>
182
+ <label id="labelWordSpacing" for="wordSpacing">Word spacing</label>
183
+ <span class="value" id="wordSpacingValue" aria-hidden="true"></span>
129
184
  </div>
130
- <input type="range" id="wordSpacing" min="0" max="40" value="0">
185
+
186
+ <input
187
+ type="range"
188
+ id="wordSpacing"
189
+ min="0"
190
+ max="40"
191
+ value="0"
192
+ aria-valuemin="0"
193
+ aria-valuemax="40"
194
+ aria-valuenow="0"
195
+ aria-labelledby="labelWordSpacing"
196
+ >
131
197
  </div>
132
198
 
133
- <div class="group" id="groupLineHeight">
199
+ <div class="group" id="groupLineHeight" role="group" aria-labelledby="labelLineHeight">
134
200
  <div class="label-row">
135
- <label for="lineHeight">Line height</label>
136
- <span class="value" id="lineHeightValue"></span>
201
+ <label id="labelLineHeight" for="lineHeight">Line height</label>
202
+ <span class="value" id="lineHeightValue" aria-hidden="true"></span>
137
203
  </div>
138
- <input type="range" id="lineHeight" min="1" max="3" step="0.1" value="1.5">
204
+
205
+ <input
206
+ type="range"
207
+ id="lineHeight"
208
+ min="1"
209
+ max="3"
210
+ step="0.1"
211
+ value="1.5"
212
+ aria-valuemin="1"
213
+ aria-valuemax="3"
214
+ aria-valuenow="1.5"
215
+ aria-labelledby="labelLineHeight"
216
+ >
139
217
  </div>
140
218
 
141
- <div class="group" id="groupContrast">
219
+ <div class="group" id="groupContrast" role="group" aria-labelledby="labelContrast">
142
220
  <div class="label-row">
143
- <label for="contrast">Contrast</label>
144
- <span class="value" id="contrastValue"></span>
221
+ <label id="labelContrast" for="contrast">Contrast</label>
222
+ <span class="value" id="contrastValue" aria-hidden="true"></span>
145
223
  </div>
146
- <input type="range" id="contrast" min="50" max="200" value="100">
224
+
225
+ <input
226
+ type="range"
227
+ id="contrast"
228
+ min="50"
229
+ max="200"
230
+ value="100"
231
+ aria-valuemin="50"
232
+ aria-valuemax="200"
233
+ aria-valuenow="100"
234
+ aria-labelledby="labelContrast"
235
+ >
147
236
  </div>
148
237
 
149
- <div class="group" id="groupFontFamily">
238
+ <div class="group" id="groupFontFamily" role="group" aria-labelledby="labelFontFamily">
150
239
  <div class="label-row">
151
- <label for="fontFamily">Font family</label>
240
+ <label id="labelFontFamily" for="fontFamily">Font family</label>
152
241
  </div>
153
- <select id="fontFamily">
242
+
243
+ <select id="fontFamily" aria-labelledby="labelFontFamily">
154
244
  <option value="system-ui, sans-serif">System</option>
155
245
  <option value="Georgia, serif">Serif</option>
156
246
  <option value="'Courier New', monospace">Monospace</option>
157
247
  <option value="Verdana, sans-serif">Verdana</option>
248
+ <option value="inherit">Inherit</option>
158
249
  </select>
159
250
  </div>
160
251
  `;
@@ -250,12 +341,21 @@ class TypographyController extends HTMLElement {
250
341
  }
251
342
 
252
343
  setValues(values = {}) {
344
+
253
345
  if (values.fontSize !== undefined) this.fontSize.value = values.fontSize;
254
346
  if (values.letterSpacing !== undefined) this.letterSpacing.value = values.letterSpacing;
255
347
  if (values.wordSpacing !== undefined) this.wordSpacing.value = values.wordSpacing;
256
348
  if (values.lineHeight !== undefined) this.lineHeight.value = values.lineHeight;
257
349
  if (values.contrast !== undefined) this.contrast.value = values.contrast;
258
- if (values.fontFamily !== undefined) this.fontFamily.value = values.fontFamily;
350
+ if (values.fontFamily !== undefined) {
351
+ if (values.fontFamily === "inherit" && target) {
352
+ const computed = getComputedStyle(target).fontFamily;
353
+ this.fontFamily.value = computed;
354
+ } else {
355
+ this.fontFamily.value = values.fontFamily;
356
+ }
357
+ }
358
+
259
359
 
260
360
  this.update();
261
361
  }