@finsweet/webflow-apps-utils 1.0.17 → 1.0.19

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.
Files changed (31) hide show
  1. package/dist/ui/components/LoadingScreen.svelte +0 -1
  2. package/dist/ui/components/button/Button.svelte +0 -1
  3. package/dist/ui/components/color-picker/ColorPicker.stories.svelte +16 -15
  4. package/dist/ui/components/color-picker/ColorPicker.svelte +93 -32
  5. package/dist/ui/components/color-picker/ColorPicker.svelte.d.ts +8 -0
  6. package/dist/ui/components/color-picker/ColorPickerWrapper.svelte +14 -0
  7. package/dist/ui/components/color-picker/ColorPickerWrapper.svelte.d.ts +6 -0
  8. package/dist/ui/components/color-picker/ColorSelect.svelte +100 -34
  9. package/dist/ui/components/copy-text/CopyText.svelte +0 -2
  10. package/dist/ui/components/input/Input.svelte +0 -1
  11. package/dist/ui/components/layout/Layout.svelte +1 -2
  12. package/dist/ui/components/layout/examples/ExampleLayout.svelte +3 -3
  13. package/dist/ui/components/modal/Modal.svelte +0 -1
  14. package/dist/ui/components/notification/Notification.svelte +2 -2
  15. package/dist/ui/components/select/Select.svelte +4 -4
  16. package/dist/ui/components/text/Text.svelte +1 -6
  17. package/dist/ui/components/tooltip/Tooltip.svelte +1 -1
  18. package/dist/ui/index.css +1 -0
  19. package/dist/ui/providers/GlobalProviderDemo.svelte +1 -3
  20. package/dist/ui/router/examples/RouterExample.svelte +0 -1
  21. package/dist/ui/router/examples/pages/AboutPage.svelte +0 -2
  22. package/dist/ui/router/examples/pages/HomePage.svelte +0 -2
  23. package/dist/ui/router/examples/pages/NotFoundPage.svelte +0 -2
  24. package/dist/ui/router/providers/Link.svelte +0 -1
  25. package/dist/ui/stores/forms/FormDemo.svelte +1 -5
  26. package/dist/ui/utils/color-utils.d.ts +12 -0
  27. package/dist/ui/utils/color-utils.js +50 -0
  28. package/dist/ui/utils/diff-mapper/DiffMapperDemo.svelte +2 -3
  29. package/dist/ui/utils/index.d.ts +1 -0
  30. package/dist/ui/utils/index.js +1 -0
  31. package/package.json +1 -1
@@ -61,7 +61,6 @@
61
61
  gap: 10px;
62
62
  color: var(--actionSecondaryText);
63
63
  text-align: center;
64
- font-family: Inter;
65
64
  font-size: 11.5px;
66
65
  font-style: normal;
67
66
  font-weight: 400;
@@ -269,7 +269,6 @@
269
269
 
270
270
  /* Styling */
271
271
  border-radius: var(--border-radius, 4px);
272
- font-family: var(--font-stack, Inter, sans-serif);
273
272
  font-weight: var(--font-weight-normal, 400);
274
273
  font-size: 11.5px;
275
274
  min-height: 24px;
@@ -39,19 +39,28 @@
39
39
  });
40
40
  </script>
41
41
 
42
- <Story name="Default" args={{}} />
42
+ <Story name="Default" args={{ oncolorchange: handleFullColorChange }} />
43
43
 
44
- <Story name="With Initial Color" args={{ color: '#00ff00' }} />
44
+ <Story
45
+ name="With Initial Color"
46
+ args={{ color: '#00ff00', oncolorchange: handleFullColorChange }}
47
+ />
45
48
 
46
- <Story name="Edge: Invalid Color" args={{ color: '#xyzxyz' }} />
49
+ <Story
50
+ name="Edge: Invalid Color"
51
+ args={{ color: '#xyzxyz', oncolorchange: handleFullColorChange }}
52
+ />
47
53
 
48
- <Story name="Edge: No Color" args={{ color: undefined }} />
54
+ <Story name="Edge: No Color" args={{ color: undefined, oncolorchange: handleFullColorChange }} />
49
55
 
50
- <Story name="Disabled Picker" args={{ color: '#ebebeb', disabled: true }} />
56
+ <Story
57
+ name="Disabled Picker"
58
+ args={{ color: '#ebebeb', disabled: true, oncolorchange: handleFullColorChange }}
59
+ />
51
60
 
52
61
  <Story
53
62
  name="Full Color Object Demo"
54
- args={{ color: '#ff6600' }}
63
+ args={{ color: '#ff6600', oncolorchange: handleFullColorChange }}
55
64
  story={{
56
65
  parameters: {
57
66
  docs: {
@@ -68,15 +77,7 @@
68
77
  name="Alpha Channel Demo"
69
78
  args={{
70
79
  color: '#ff0000',
71
- oncolorchange: (fullColor: {
72
- hex: string;
73
- rgb: { r: number; g: number; b: number; value: string };
74
- rgba: { r: number; g: number; b: number; a: number; value: string };
75
- hsb: { h: number; s: number; b: number; value: string };
76
- alpha: number;
77
- }) => {
78
- console.log('Full color object:', fullColor);
79
- }
80
+ oncolorchange: handleFullColorChange
80
81
  }}
81
82
  story={{
82
83
  parameters: {
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { normalizeHex } from '../../utils';
2
3
  import Tooltip from '../tooltip/Tooltip.svelte';
3
4
  import ColorSelect from './ColorSelect.svelte';
4
5
 
@@ -28,23 +29,48 @@
28
29
  * Whether the picker is disabled.
29
30
  */
30
31
  disabled?: boolean;
32
+
33
+ /**
34
+ * The id of the picker.
35
+ */
36
+ id?: string;
37
+
38
+ /**
39
+ * Whether the color select should be shown by default.
40
+ */
41
+ defaultShowColorSelect?: boolean;
31
42
  }
32
43
 
33
44
  let {
34
45
  color = $bindable('#fff'),
35
46
  oncolorchange,
36
47
  width = '80px',
37
- disabled = false
48
+ disabled = false,
49
+ defaultShowColorSelect = false,
50
+ id
38
51
  }: Props = $props();
39
52
 
40
- function normalizeHex(value: string): string {
41
- let v = value.trim();
42
-
43
- if (!v.startsWith('#')) v = `#${v}`;
44
-
45
- // Keep original case for consistency with tests
46
- return v;
47
- }
53
+ // Remove local inputValue state
54
+ // let inputValue = $state(color);
55
+
56
+ // Helper: normalize hex to 6-digit uppercase
57
+ // function normalizeHex(value: string): string {
58
+ // if (/^#[A-Fa-f0-9]{3}$/.test(value)) {
59
+ // return (
60
+ // '#' +
61
+ // value
62
+ // .slice(1)
63
+ // .split('')
64
+ // .map((c) => c + c)
65
+ // .join('')
66
+ // .toUpperCase()
67
+ // );
68
+ // }
69
+ // if (/^#[A-Fa-f0-9]{6}$/.test(value)) {
70
+ // return value.toUpperCase();
71
+ // }
72
+ // return value;
73
+ // }
48
74
 
49
75
  function isValidColor(value: string): boolean {
50
76
  const hexRegex = /^#?([A-Fa-f0-9]{3}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$/;
@@ -107,6 +133,41 @@
107
133
  };
108
134
  }
109
135
 
136
+ function normalizeHexTo6Upper(value: string): string {
137
+ const hex = value.startsWith('#') ? value : `#${value}`;
138
+ if (/^#[A-Fa-f0-9]{3}$/.test(hex)) {
139
+ return (
140
+ '#' +
141
+ hex
142
+ .slice(1)
143
+ .split('')
144
+ .map((c) => c + c)
145
+ .join('')
146
+ .toUpperCase()
147
+ );
148
+ }
149
+ if (/^#[A-Fa-f0-9]{6}$/.test(hex)) {
150
+ return hex.toUpperCase();
151
+ }
152
+ return value;
153
+ }
154
+
155
+ $effect(() => {
156
+ // Only update inputValue if color changes externally (not from input)
157
+ // if (
158
+ // color !== inputValue &&
159
+ // document.activeElement !== null &&
160
+ // document.activeElement !== inputEl
161
+ // ) {
162
+ // inputValue = color;
163
+ // }
164
+ });
165
+
166
+ function handleInput(event: Event) {
167
+ const target = event.target as HTMLInputElement;
168
+ color = target.value;
169
+ }
170
+
110
171
  function handleInputPaste(event: ClipboardEvent) {
111
172
  event.preventDefault();
112
173
 
@@ -116,23 +177,9 @@
116
177
 
117
178
  cleanText = cleanText.substring(0, 9);
118
179
 
119
- if (isValidColor(cleanText)) {
120
- const normalizedValue = normalizeHex(cleanText);
180
+ const normalizedValue = normalizeHexTo6Upper(cleanText);
181
+ if (/^#[A-F0-9]{6}$/.test(normalizedValue)) {
121
182
  color = normalizedValue;
122
- // Call oncolorchange with the new color
123
- const colorObject = createColorObject(normalizedValue);
124
- oncolorchange?.(colorObject);
125
- }
126
- }
127
-
128
- function handleInputChange(event: Event) {
129
- const target = event.target as HTMLInputElement;
130
- const value = target.value.trim();
131
-
132
- if (isValidColor(value)) {
133
- const normalizedValue = normalizeHex(value);
134
- color = normalizedValue;
135
- // Call oncolorchange with the new color
136
183
  const colorObject = createColorObject(normalizedValue);
137
184
  oncolorchange?.(colorObject);
138
185
  }
@@ -140,18 +187,27 @@
140
187
 
141
188
  function handleInputKeydown(event: KeyboardEvent) {
142
189
  if (event.key === 'Enter') {
143
- (event.target as HTMLInputElement).blur();
190
+ // (event.target as HTMLInputElement).blur(); // Removed local inputValue update
144
191
  }
145
192
  }
146
193
 
147
- function handleColorChange(newColor: string) {
148
- const normalizedValue = normalizeHex(newColor);
149
- color = normalizedValue;
194
+ function handleBlur(event: Event) {
195
+ const target = event.target as HTMLInputElement;
196
+ const normalized = normalizeHex(target.value);
197
+ if (/^#[A-F0-9]{6}$/.test(normalized)) {
198
+ color = normalized;
199
+ const colorObject = createColorObject(normalized);
200
+ oncolorchange?.(colorObject);
201
+ }
150
202
  }
151
203
 
152
204
  function handleFullColorChange(fullColor: ColorObject) {
153
205
  oncolorchange?.(fullColor);
206
+ const normalizedValue = normalizeHex(fullColor.hex);
207
+ color = normalizedValue;
154
208
  }
209
+
210
+ let showColorSelect = $state(defaultShowColorSelect);
155
211
  </script>
156
212
 
157
213
  <div class="color-picker">
@@ -164,6 +220,8 @@
164
220
  stopPropagation={true}
165
221
  width="241px"
166
222
  placement="bottom"
223
+ onshow={() => (showColorSelect = true)}
224
+ onclose={() => (showColorSelect = false)}
167
225
  fallbackPlacements={['top-end', 'top', 'bottom-end', 'bottom', 'top-start', 'bottom-start']}
168
226
  >
169
227
  {#snippet target()}
@@ -172,7 +230,9 @@
172
230
  </div>
173
231
  {/snippet}
174
232
  {#snippet tooltip()}
175
- <ColorSelect {color} oncolorchange={handleFullColorChange} />
233
+ {#if showColorSelect}
234
+ <ColorSelect bind:color oncolorchange={handleFullColorChange} />
235
+ {/if}
176
236
  {/snippet}
177
237
  </Tooltip>
178
238
 
@@ -183,12 +243,14 @@
183
243
  bind:value={color}
184
244
  {disabled}
185
245
  readonly={disabled}
186
- oninput={handleInputChange}
246
+ oninput={handleInput}
187
247
  onkeydown={handleInputKeydown}
188
248
  onpaste={handleInputPaste}
249
+ onblur={handleBlur}
189
250
  placeholder="#ffffff"
190
251
  aria-label="Color hex value"
191
252
  style="width: {width}"
253
+ {id}
192
254
  />
193
255
  </div>
194
256
 
@@ -228,7 +290,6 @@
228
290
  width: 80px;
229
291
  padding: 4px 8px 4px 0px;
230
292
  border-radius: 4px;
231
- font-family: monospace;
232
293
  font-size: 12px;
233
294
  border: none;
234
295
  background: transparent;
@@ -38,6 +38,14 @@ interface Props {
38
38
  * Whether the picker is disabled.
39
39
  */
40
40
  disabled?: boolean;
41
+ /**
42
+ * The id of the picker.
43
+ */
44
+ id?: string;
45
+ /**
46
+ * Whether the color select should be shown by default.
47
+ */
48
+ defaultShowColorSelect?: boolean;
41
49
  }
42
50
  declare const ColorPicker: import("svelte").Component<Props, {}, "color">;
43
51
  type ColorPicker = ReturnType<typeof ColorPicker>;
@@ -0,0 +1,14 @@
1
+ <script lang="ts">
2
+ import ColorPicker from './ColorPicker.svelte';
3
+ let { initialColor = '#000000', oncolorchange } = $props();
4
+ let color = $state(initialColor);
5
+
6
+ //eslint-disable-next-line @typescript-eslint/no-explicit-any
7
+ function handleColorChange(fullColor: any) {
8
+ color = fullColor.hex;
9
+ oncolorchange?.(fullColor);
10
+ }
11
+ </script>
12
+
13
+ <label for="color-input">Color hex value</label>
14
+ <ColorPicker id="color-input" bind:color oncolorchange={handleColorChange} />
@@ -0,0 +1,6 @@
1
+ declare const ColorPickerWrapper: import("svelte").Component<{
2
+ initialColor?: string;
3
+ oncolorchange: any;
4
+ }, {}, "">;
5
+ type ColorPickerWrapper = ReturnType<typeof ColorPickerWrapper>;
6
+ export default ColorPickerWrapper;
@@ -1,5 +1,7 @@
1
1
  <script lang="ts">
2
- import { onMount } from 'svelte';
2
+ import { onMount, tick } from 'svelte';
3
+
4
+ import { normalizeHex } from '../../utils/color-utils';
3
5
 
4
6
  // Color object type definition
5
7
  interface ColorObject {
@@ -83,6 +85,29 @@
83
85
  let dragStartPosition = $state<{ x: number; y: number } | null>(null);
84
86
  const DRAG_THRESHOLD = 3; // pixels of movement before considering it a drag
85
87
 
88
+ // Helper function to normalize color input to hex
89
+ function normalizeColorToHex(colorInput: string): string {
90
+ // If it's already a valid hex, normalize it
91
+ if (colorInput.startsWith('#')) {
92
+ return normalizeHex(colorInput);
93
+ }
94
+
95
+ // For named colors, use a canvas to convert to hex
96
+ const canvas = document.createElement('canvas');
97
+ const ctx = canvas.getContext('2d');
98
+ if (ctx) {
99
+ ctx.fillStyle = colorInput;
100
+ const computedColor = ctx.fillStyle;
101
+ // ctx.fillStyle returns hex format for valid colors
102
+ if (computedColor.startsWith('#')) {
103
+ return computedColor.toUpperCase();
104
+ }
105
+ }
106
+
107
+ // Fallback to original value if conversion fails
108
+ return normalizeHex(colorInput);
109
+ }
110
+
86
111
  // Color conversion utilities
87
112
  function hsbToRgb(h: number, s: number, b: number): [number, number, number] {
88
113
  h = h / 360;
@@ -211,10 +236,26 @@
211
236
  return result;
212
237
  }
213
238
 
214
- // For non-hex colors, only accept valid hex patterns (without #)
215
- const hexRegex = /^([0-9A-Fa-f]{3}|[0-9A-Fa-f]{4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
216
- const result = hexRegex.test(value);
217
- return result;
239
+ // For non-hex colors, try to validate using canvas
240
+ try {
241
+ const canvas = document.createElement('canvas');
242
+ const ctx = canvas.getContext('2d');
243
+ if (ctx) {
244
+ ctx.fillStyle = value;
245
+ return (
246
+ ctx.fillStyle !== '#000000' ||
247
+ value === 'black' ||
248
+ value === '#000000' ||
249
+ value === '#000'
250
+ );
251
+ }
252
+ } catch (e) {
253
+ // Fallback to regex for hex without #
254
+ const hexRegex = /^([0-9A-Fa-f]{3}|[0-9A-Fa-f]{4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
255
+ return hexRegex.test(value);
256
+ }
257
+
258
+ return false;
218
259
  }
219
260
 
220
261
  function rgbToHsb(r: number, g: number, b: number): [number, number, number] {
@@ -266,8 +307,16 @@
266
307
  const rect = colorWell.getBoundingClientRect();
267
308
  const x = Math.max(0, Math.min(rect.width, event.clientX - rect.left));
268
309
  const y = Math.max(0, Math.min(rect.height, event.clientY - rect.top));
310
+
311
+ // Always update HSB values first
269
312
  saturation = Math.round((x / rect.width) * 100);
270
313
  brightness = Math.round(100 - (y / rect.height) * 100);
314
+
315
+ // If in RGB mode, sync the RGB values with the new HSB values
316
+ if (mode === 'RGB') {
317
+ [rgbRed, rgbGreen, rgbBlue] = hsbToRgb(hue, saturation, brightness);
318
+ }
319
+
271
320
  updateColor();
272
321
  updateColorPickerPosition();
273
322
  }
@@ -276,16 +325,31 @@
276
325
  if (!hueBar) return;
277
326
  const rect = hueBar.getBoundingClientRect();
278
327
  const x = Math.max(0, Math.min(rect.width, event.clientX - rect.left));
328
+
329
+ // Always update HSB hue first
279
330
  hue = Math.round((x / rect.width) * 360);
331
+
332
+ // If in RGB mode, sync the RGB values with the new HSB values
333
+ if (mode === 'RGB') {
334
+ [rgbRed, rgbGreen, rgbBlue] = hsbToRgb(hue, saturation, brightness);
335
+ }
336
+
280
337
  updateColor();
281
338
  }
282
339
 
283
340
  function handleAlphaBarInteraction(event: MouseEvent) {
284
341
  if (!alphaBar) return;
342
+
285
343
  const rect = alphaBar.getBoundingClientRect();
286
344
  const x = Math.max(0, Math.min(rect.width, event.clientX - rect.left));
287
- alpha = Math.round((x / rect.width) * 100);
288
- emitColorChange(hexValue, alpha);
345
+
346
+ let newAlpha = Math.round((x / rect.width) * 100);
347
+
348
+ if (isNaN(newAlpha) || typeof newAlpha !== 'number') newAlpha = 100;
349
+
350
+ alpha = newAlpha;
351
+
352
+ updateColor();
289
353
  }
290
354
 
291
355
  function handleHexChange(event: Event) {
@@ -442,18 +506,18 @@
442
506
  return min + (value * (max - min)) / range;
443
507
  }
444
508
 
445
- // Watch for prop changes
509
+ // Watch for prop changes - FIXED: Better color normalization
446
510
  $effect(() => {
447
- if (color !== hexValue) {
448
- if (isValidColor(color)) {
449
- hexValue = color;
450
- const [h, s, b] = hexToHsb(color);
451
- hue = h;
452
- saturation = s;
453
- brightness = b;
454
- [rgbRed, rgbGreen, rgbBlue] = hsbToRgb(hue, saturation, brightness);
455
- updateColorPickerPosition();
456
- }
511
+ const normalized = normalizeColorToHex(color);
512
+ if (normalized !== hexValue) {
513
+ hexValue = normalized;
514
+ const [h, s, b] = hexToHsb(normalized);
515
+ hue = h;
516
+ saturation = s;
517
+ brightness = b;
518
+ [rgbRed, rgbGreen, rgbBlue] = hsbToRgb(hue, saturation, brightness);
519
+ // Force position update after state changes
520
+ setTimeout(() => updateColorPickerPosition(), 0);
457
521
  }
458
522
  });
459
523
 
@@ -462,18 +526,19 @@
462
526
  );
463
527
 
464
528
  onMount(() => {
465
- // Initialize from color prop
466
- if (isValidColor(color)) {
467
- hexValue = color;
468
- const [h, s, b] = hexToHsb(color);
469
- hue = h;
470
- saturation = s;
471
- brightness = b;
472
- [rgbRed, rgbGreen, rgbBlue] = hsbToRgb(hue, saturation, brightness);
473
- }
474
-
529
+ const normalized = normalizeColorToHex(color);
530
+ hexValue = normalized;
531
+ const [h, s, b] = hexToHsb(normalized);
532
+ hue = h;
533
+ saturation = s;
534
+ brightness = b;
535
+ [rgbRed, rgbGreen, rgbBlue] = hsbToRgb(hue, saturation, brightness);
536
+
537
+ console.log('ColorSelect mounted', { color });
475
538
  updateColor();
476
- updateColorPickerPosition();
539
+ // Delay position update to ensure DOM is ready
540
+
541
+ setTimeout(() => updateColorPickerPosition(), 5);
477
542
 
478
543
  document.addEventListener('mousemove', handleMouseMove);
479
544
  document.addEventListener('mouseup', handleMouseUp);
@@ -511,6 +576,7 @@
511
576
  }
512
577
  }
513
578
 
579
+ // Add an $effect to always update the color well and picker position after state changes
514
580
  $effect(() => {
515
581
  updateColorPickerPosition();
516
582
  });
@@ -744,7 +810,9 @@
744
810
  oninput={(e) => {
745
811
  const target = e.target as HTMLInputElement;
746
812
  if (!target) return;
747
- alpha = +target.value;
813
+ let newAlpha = +target.value;
814
+ if (isNaN(newAlpha) || typeof newAlpha !== 'number') newAlpha = 100;
815
+ alpha = newAlpha;
748
816
  emitColorChange(hexValue, alpha);
749
817
  }}
750
818
  />
@@ -928,7 +996,7 @@
928
996
  /* Controls */
929
997
  .controls {
930
998
  display: grid;
931
- grid-template-columns: 70px 1fr 30px;
999
+ grid-template-columns: 80px 1fr 30px;
932
1000
  gap: 7px;
933
1001
  width: 100%;
934
1002
  }
@@ -980,7 +1048,6 @@
980
1048
  border-radius: 4px;
981
1049
  color: var(--text1, #ebebeb);
982
1050
  font-size: 11px;
983
- font-family: Inter, sans-serif;
984
1051
  font-weight: 400;
985
1052
  line-height: 16px;
986
1053
  outline: none;
@@ -988,7 +1055,7 @@
988
1055
  }
989
1056
 
990
1057
  .input--hex {
991
- width: 70px;
1058
+ width: 80px;
992
1059
  text-align: left;
993
1060
  }
994
1061
 
@@ -1000,7 +1067,6 @@
1000
1067
  .label {
1001
1068
  color: var(--text1, #ebebeb);
1002
1069
  font-size: 11px;
1003
- font-family: Inter, sans-serif;
1004
1070
  font-weight: 400;
1005
1071
  line-height: 16px;
1006
1072
  text-align: center;
@@ -167,7 +167,6 @@
167
167
  .copy-text__title {
168
168
  margin: 0;
169
169
  color: var(--text-color-primary, #ffffff);
170
- font-family: Inter, sans-serif;
171
170
  font-size: 12px;
172
171
  font-weight: 500;
173
172
  line-height: 16px;
@@ -181,7 +180,6 @@
181
180
  align-items: flex-start;
182
181
  align-self: stretch;
183
182
  color: var(--text2);
184
- font-family: Inter;
185
183
  font-size: 12px;
186
184
  font-style: normal;
187
185
  font-weight: 500;
@@ -628,7 +628,6 @@
628
628
  gap: var(--border-radius, 2px);
629
629
  flex: 1 0 0;
630
630
  color: var(--text3);
631
- font-family: Inter;
632
631
  font-size: 11.5px;
633
632
  font-style: normal;
634
633
  font-weight: 400;
@@ -327,7 +327,6 @@
327
327
 
328
328
  .tab-text {
329
329
  color: inherit;
330
- font-family: Inter;
331
330
  font-size: 11.5px;
332
331
  font-style: normal;
333
332
  font-weight: 400;
@@ -402,7 +401,7 @@
402
401
 
403
402
  .preview-bar-content span {
404
403
  color: var(--actionPrimaryText);
405
- font-family: Inter;
404
+
406
405
  font-size: 11.5px;
407
406
  font-style: normal;
408
407
  font-weight: 500;
@@ -462,7 +462,7 @@
462
462
  font-weight: 500;
463
463
  font-size: 11.5px;
464
464
  color: var(--text1);
465
- font-family: var(--font-stack);
465
+
466
466
  text-align: center;
467
467
  }
468
468
 
@@ -474,7 +474,7 @@
474
474
  background: var(--background1);
475
475
  color: var(--text1);
476
476
  font-size: 11.5px;
477
- font-family: var(--font-stack);
477
+
478
478
  box-shadow: var(--boxShadows-input-inner);
479
479
  transition: all 0.2s ease;
480
480
  }
@@ -530,7 +530,7 @@
530
530
  flex: 1;
531
531
  font-size: 11.5px;
532
532
  color: var(--text1);
533
- font-family: var(--font-stack);
533
+
534
534
  line-height: 1.2;
535
535
  }
536
536
 
@@ -439,7 +439,6 @@
439
439
  user-select: none;
440
440
  width: 24px;
441
441
  padding: 0px 4px;
442
- font-family: inherit;
443
442
  font-size: inherit;
444
443
  position: relative;
445
444
  display: flex;
@@ -237,7 +237,7 @@
237
237
 
238
238
  .message {
239
239
  color: var(--text-text-2, #bdbdbd);
240
- font-family: Inter;
240
+
241
241
  font-size: 11px;
242
242
  font-style: normal;
243
243
  font-weight: 400;
@@ -252,7 +252,7 @@
252
252
 
253
253
  .link {
254
254
  color: var(--blue-blue-text, #8ac2ff);
255
- font-family: Inter;
255
+
256
256
  font-size: 11px;
257
257
  font-style: normal;
258
258
  font-weight: 400;
@@ -553,7 +553,7 @@
553
553
  }
554
554
  .label .label-name {
555
555
  color: var(--text1);
556
- font-family: Inter;
556
+
557
557
  font-size: 11.5px;
558
558
  font-style: normal;
559
559
  font-weight: 400;
@@ -566,7 +566,7 @@
566
566
 
567
567
  .label .label-description {
568
568
  color: var(--text2, #bdbdbd);
569
- font-family: Inter;
569
+
570
570
  font-size: 10px;
571
571
  font-style: normal;
572
572
  font-weight: 400;
@@ -645,7 +645,7 @@
645
645
  gap: 4px;
646
646
  align-self: stretch;
647
647
  color: var(--text2, #bdbdbd);
648
- font-family: Inter;
648
+
649
649
  font-size: 11px;
650
650
  font-style: normal;
651
651
  font-weight: 500;
@@ -679,7 +679,7 @@
679
679
  gap: 4px;
680
680
  align-self: stretch;
681
681
  color: var(--text-text-1, #d9d9d9);
682
- font-family: Inter;
682
+
683
683
  font-size: 11px;
684
684
  font-style: normal;
685
685
  font-weight: 400;
@@ -813,10 +813,6 @@
813
813
  {/if}
814
814
 
815
815
  <style>
816
- .text {
817
- font-family: inherit;
818
- }
819
-
820
816
  .labels.link {
821
817
  cursor: pointer;
822
818
  padding: 4px;
@@ -882,7 +878,6 @@
882
878
  }
883
879
 
884
880
  * {
885
- font-family: Inter;
886
881
  font-style: normal;
887
882
  line-height: 16px;
888
883
  letter-spacing: -0.115px;
@@ -950,7 +945,7 @@
950
945
 
951
946
  .popup-description span {
952
947
  color: var(--text2, #bdbdbd);
953
- font-family: Inter;
948
+
954
949
  font-size: 11px;
955
950
  font-style: normal;
956
951
  font-weight: 400;
@@ -382,7 +382,7 @@
382
382
  0px -0.5px 0.5px 0px rgb(0 0 0 / 0%) inset,
383
383
  0px 0.5px 0.5px 0px rgb(255 255 255 / 34%) inset;
384
384
  color: var(--text2, #b9b9b9);
385
- font-family: Inter;
385
+
386
386
  line-height: 120%;
387
387
  width: 150px;
388
388
  padding: 6px;
package/dist/ui/index.css CHANGED
@@ -159,6 +159,7 @@ body,
159
159
  #app {
160
160
  height: 100%;
161
161
  background: var(--background1);
162
+ font-family: var(--font-stack);
162
163
  /* Prevents layout shift when scrollbar appears, does not work in Safari browsers */
163
164
  /* scrollbar-gutter: stable; */
164
165
  }
@@ -161,7 +161,6 @@
161
161
  margin: 0 auto;
162
162
  background: var(--background1);
163
163
  color: var(--text1);
164
- font-family: var(--font-stack);
165
164
  }
166
165
 
167
166
  .start-demo {
@@ -211,7 +210,7 @@
211
210
  cursor: pointer;
212
211
  font-size: var(--font-size-small);
213
212
  font-weight: var(--font-weight-normal);
214
- font-family: var(--font-stack);
213
+
215
214
  transition: all 0.2s ease;
216
215
  }
217
216
 
@@ -252,7 +251,6 @@
252
251
  border: 1px solid var(--border2);
253
252
  padding: var(--padding-tiny);
254
253
  border-radius: var(--border-radius);
255
- font-family: monospace;
256
254
  font-size: var(--font-size-small);
257
255
  color: var(--text2);
258
256
  display: block;
@@ -130,7 +130,6 @@
130
130
  max-width: 800px;
131
131
  margin: 0 auto;
132
132
  padding: 1rem;
133
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
134
133
  }
135
134
 
136
135
  .app-layout {
@@ -290,7 +290,6 @@
290
290
  cursor: pointer;
291
291
  transition: all 0.3s ease;
292
292
  width: 100%;
293
- font-family: inherit;
294
293
  font-size: inherit;
295
294
  }
296
295
 
@@ -553,7 +552,6 @@
553
552
 
554
553
  .debug-info p {
555
554
  margin: 0.5rem 0;
556
- font-family: 'SFMono-Regular', Consolas, monospace;
557
555
  font-size: 0.875rem;
558
556
  color: #495057;
559
557
  }
@@ -169,7 +169,6 @@
169
169
  background: #e9ecef;
170
170
  padding: 0.2rem 0.4rem;
171
171
  border-radius: 3px;
172
- font-family: 'SFMono-Regular', Consolas, monospace;
173
172
  font-size: 0.875rem;
174
173
  color: #e83e8c;
175
174
  }
@@ -189,7 +188,6 @@
189
188
 
190
189
  .debug-info p {
191
190
  margin: 0.5rem 0;
192
- font-family: 'SFMono-Regular', Consolas, monospace;
193
191
  font-size: 0.875rem;
194
192
  color: #495057;
195
193
  }
@@ -132,7 +132,6 @@
132
132
  background: #e9ecef;
133
133
  padding: 0.2rem 0.4rem;
134
134
  border-radius: 3px;
135
- font-family: 'SFMono-Regular', Consolas, monospace;
136
135
  color: #e83e8c;
137
136
  font-size: 0.875rem;
138
137
  }
@@ -273,7 +272,6 @@
273
272
 
274
273
  .debug-info p {
275
274
  margin: 0.5rem 0;
276
- font-family: 'SFMono-Regular', Consolas, monospace;
277
275
  font-size: 0.875rem;
278
276
  color: #495057;
279
277
  }
@@ -143,7 +143,6 @@
143
143
  cursor: pointer;
144
144
  border: none;
145
145
  background: none;
146
- font: inherit;
147
146
  color: inherit;
148
147
  }
149
148
 
@@ -296,7 +296,6 @@
296
296
  margin: 0 auto;
297
297
  background: var(--background1);
298
298
  color: var(--text1);
299
- font-family: var(--font-stack);
300
299
  }
301
300
 
302
301
  .demo-controls {
@@ -375,7 +374,6 @@
375
374
  background: var(--background1);
376
375
  color: var(--text1);
377
376
  font-size: var(--font-size-small);
378
- font-family: var(--font-stack);
379
377
  }
380
378
 
381
379
  input:focus {
@@ -413,7 +411,7 @@
413
411
  cursor: pointer;
414
412
  font-size: var(--font-size-small);
415
413
  font-weight: var(--font-weight-normal);
416
- font-family: var(--font-stack);
414
+
417
415
  transition: all 0.2s ease;
418
416
  }
419
417
 
@@ -509,7 +507,6 @@
509
507
  background: var(--background3);
510
508
  border: 1px solid var(--border2);
511
509
  border-radius: var(--border-radius);
512
- font-family: monospace;
513
510
  font-size: var(--font-size-tiny);
514
511
  color: var(--text3);
515
512
  }
@@ -519,7 +516,6 @@
519
516
  border: 1px solid var(--border2);
520
517
  padding: var(--padding-tiny);
521
518
  border-radius: var(--border-radius);
522
- font-family: monospace;
523
519
  font-size: var(--font-size-tiny);
524
520
  color: var(--text2);
525
521
  display: block;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Convert a color name to a hex value
3
+ * @param colorName - The color name to convert
4
+ * @returns The hex value of the color
5
+ */
6
+ export declare function colorNameToHex(colorName: string): string | null;
7
+ /**
8
+ * Normalize a hex value
9
+ * @param value - The hex value to normalize
10
+ * @returns The normalized hex value
11
+ */
12
+ export declare function normalizeHex(value: string): string;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Convert a color name to a hex value
3
+ * @param colorName - The color name to convert
4
+ * @returns The hex value of the color
5
+ */
6
+ export function colorNameToHex(colorName) {
7
+ const tempDiv = document.createElement('div');
8
+ tempDiv.style.color = colorName;
9
+ document.body.appendChild(tempDiv);
10
+ const rgbColor = window.getComputedStyle(tempDiv).color;
11
+ document.body.removeChild(tempDiv);
12
+ const match = rgbColor.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
13
+ if (match) {
14
+ const r = parseInt(match[1]);
15
+ const g = parseInt(match[2]);
16
+ const b = parseInt(match[3]);
17
+ const toHex = (c) => {
18
+ const hex = c.toString(16);
19
+ return hex.length === 1 ? '0' + hex : hex;
20
+ };
21
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
22
+ }
23
+ else {
24
+ return null;
25
+ }
26
+ }
27
+ /**
28
+ * Normalize a hex value
29
+ * @param value - The hex value to normalize
30
+ * @returns The normalized hex value
31
+ */
32
+ export function normalizeHex(value) {
33
+ let v = value.trim();
34
+ if (!v.startsWith('#')) {
35
+ const hex = colorNameToHex(v);
36
+ if (hex)
37
+ v = hex;
38
+ else
39
+ v = `#${v}`;
40
+ }
41
+ // Expand 3-digit hex to 6-digit
42
+ if (/^#[A-Fa-f0-9]{3}$/.test(v)) {
43
+ v = '#' + v[1] + v[1] + v[2] + v[2] + v[3] + v[3];
44
+ }
45
+ // Only accept valid 6-digit hex
46
+ if (/^#[A-Fa-f0-9]{6}$/.test(v)) {
47
+ return v.toUpperCase();
48
+ }
49
+ return value;
50
+ }
@@ -234,7 +234,7 @@ See console for the complete diff structure.
234
234
  <style>
235
235
  .demo-container {
236
236
  padding: var(--spacing-24);
237
- font-family: var(--font-stack);
237
+
238
238
  background: var(--background1);
239
239
  color: var(--text1);
240
240
  max-width: 800px;
@@ -261,7 +261,7 @@ See console for the complete diff structure.
261
261
  cursor: pointer;
262
262
  font-size: var(--font-size-small);
263
263
  font-weight: var(--font-weight-normal);
264
- font-family: var(--font-stack);
264
+
265
265
  transition: all 0.2s ease;
266
266
  margin: var(--spacing-8) 0;
267
267
  }
@@ -284,7 +284,6 @@ See console for the complete diff structure.
284
284
  margin-top: var(--spacing-12);
285
285
  white-space: pre-wrap;
286
286
  border-radius: var(--border-radius);
287
- font-family: monospace;
288
287
  color: var(--text2);
289
288
  font-size: var(--font-size-small);
290
289
  }
@@ -1,3 +1,4 @@
1
1
  export * from './auth';
2
2
  export * from './diff-mapper';
3
3
  export * from './helpers';
4
+ export * from './color-utils';
@@ -1,3 +1,4 @@
1
1
  export * from './auth';
2
2
  export * from './diff-mapper';
3
3
  export * from './helpers';
4
+ export * from './color-utils';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finsweet/webflow-apps-utils",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
4
4
  "description": "Shared utilities for Webflow apps",
5
5
  "homepage": "https://github.com/finsweet/webflow-apps-utils",
6
6
  "repository": {