fetta 1.1.0 → 1.1.2

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
@@ -85,7 +85,6 @@ const result = splitText(element, options);
85
85
  | `onSplit` | `function` | — | Callback after initial split |
86
86
  | `revertOnComplete` | `boolean` | `false` | Auto-revert when animation completes |
87
87
  | `propIndex` | `boolean` | `false` | Add CSS custom properties: `--char-index`, `--word-index`, `--line-index` |
88
- | `willChange` | `boolean` | `false` | Add `will-change: transform, opacity` for performance |
89
88
 
90
89
  #### Return Value
91
90
 
@@ -111,7 +110,7 @@ import { SplitText } from 'fetta/react';
111
110
  | `children` | `ReactElement` | — | Single element to split |
112
111
  | `onSplit` | `function` | — | Called after text is split |
113
112
  | `onResize` | `function` | — | Called on autoSplit re-split |
114
- | `options` | `object` | — | Split options (type, classes, mask, propIndex, willChange) |
113
+ | `options` | `object` | — | Split options (type, classes, mask, propIndex) |
115
114
  | `autoSplit` | `boolean` | `false` | Re-split on container resize |
116
115
  | `revertOnComplete` | `boolean` | `false` | Revert after animation completes |
117
116
  | `inView` | `boolean \| InViewOptions` | `false` | Enable viewport detection |
@@ -69,14 +69,16 @@ function injectSrOnlyStyles() {
69
69
  if (srOnlyStylesInjected || typeof document === "undefined") return;
70
70
  const style = document.createElement("style");
71
71
  style.textContent = `
72
- .fetta-sr-only:not(:focus):not(:active) {
73
- clip: rect(0 0 0 0);
74
- clip-path: inset(50%);
72
+ .fetta-sr-only {
73
+ position: absolute;
74
+ width: 1px;
75
75
  height: 1px;
76
+ padding: 0;
77
+ margin: -1px;
76
78
  overflow: hidden;
77
- position: absolute;
79
+ clip-path: inset(50%);
78
80
  white-space: nowrap;
79
- width: 1px;
81
+ border-width: 0;
80
82
  }`;
81
83
  document.head.appendChild(style);
82
84
  srOnlyStylesInjected = true;
@@ -257,8 +259,8 @@ function createSpan(className, index, display = "inline-block", options) {
257
259
  span.style.display = display;
258
260
  span.style.position = "relative";
259
261
  span.style.textDecoration = "inherit";
260
- if (options == null ? void 0 : options.willChange) {
261
- span.style.willChange = "transform, opacity";
262
+ if (options == null ? void 0 : options.ariaHidden) {
263
+ span.setAttribute("aria-hidden", "true");
262
264
  }
263
265
  return span;
264
266
  }
@@ -306,8 +308,8 @@ function performSplit(element, measuredWords, charClass, wordClass, lineClass, s
306
308
  measuredWords.forEach((measuredWord, wordIndex) => {
307
309
  const wordSpan = createSpan(wordClass, wordIndex, "inline-block", {
308
310
  propIndex: options == null ? void 0 : options.propIndex,
309
- willChange: options == null ? void 0 : options.willChange,
310
- propName: "word"
311
+ propName: "word",
312
+ ariaHidden: options == null ? void 0 : options.ariaHidden
311
313
  });
312
314
  if (measuredWord.noSpaceBefore) {
313
315
  noSpaceBeforeSet.add(wordSpan);
@@ -318,8 +320,8 @@ function performSplit(element, measuredWords, charClass, wordClass, lineClass, s
318
320
  measuredWord.chars.forEach((measuredChar, charIndexInWord) => {
319
321
  const charSpan = createSpan(charClass, globalCharIndex, "inline-block", {
320
322
  propIndex: options == null ? void 0 : options.propIndex,
321
- willChange: options == null ? void 0 : options.willChange,
322
- propName: "char"
323
+ propName: "char",
324
+ ariaHidden: options == null ? void 0 : options.ariaHidden
323
325
  });
324
326
  charSpan.textContent = measuredChar.char;
325
327
  globalCharIndex++;
@@ -348,7 +350,6 @@ function performSplit(element, measuredWords, charClass, wordClass, lineClass, s
348
350
  const charIndexInWord = measuredWord.chars.indexOf(measuredChar);
349
351
  const charSpan = createSpan(charClass, globalCharIndex, "inline-block", {
350
352
  propIndex: options == null ? void 0 : options.propIndex,
351
- willChange: options == null ? void 0 : options.willChange,
352
353
  propName: "char"
353
354
  });
354
355
  charSpan.textContent = measuredChar.char;
@@ -455,7 +456,17 @@ function performSplit(element, measuredWords, charClass, wordClass, lineClass, s
455
456
  }
456
457
  }
457
458
  if (splitChars && allChars.length > 1 && !isSafari) {
458
- const positions = allChars.map((c) => c.getBoundingClientRect().left);
459
+ const range = document.createRange();
460
+ const positions = allChars.map((c) => {
461
+ var _a;
462
+ const textNode = c.firstChild;
463
+ if (textNode && textNode.nodeType === Node.TEXT_NODE) {
464
+ range.setStart(textNode, 0);
465
+ range.setEnd(textNode, ((_a = textNode.textContent) == null ? void 0 : _a.length) || 1);
466
+ return range.getBoundingClientRect().left;
467
+ }
468
+ return c.getBoundingClientRect().left;
469
+ });
459
470
  for (let i2 = 1; i2 < allChars.length; i2++) {
460
471
  const charSpan = allChars[i2];
461
472
  const expectedGap = charSpan.dataset.expectedGap;
@@ -463,10 +474,9 @@ function performSplit(element, measuredWords, charClass, wordClass, lineClass, s
463
474
  const originalGap = parseFloat(expectedGap);
464
475
  const currentGap = positions[i2] - positions[i2 - 1];
465
476
  const delta = originalGap - currentGap;
466
- if (Math.abs(delta) < 20) {
467
- const roundedDelta = Math.round(delta * 100) / 100;
477
+ if (Math.abs(delta) > 0.1 && Math.abs(delta) < 20) {
468
478
  const targetElement = (options == null ? void 0 : options.mask) === "chars" && charSpan.parentElement ? charSpan.parentElement : charSpan;
469
- targetElement.style.marginLeft = `${roundedDelta}px`;
479
+ targetElement.style.marginLeft = `${delta}px`;
470
480
  }
471
481
  delete charSpan.dataset.expectedGap;
472
482
  }
@@ -479,8 +489,8 @@ function performSplit(element, measuredWords, charClass, wordClass, lineClass, s
479
489
  lineGroups.forEach((words, lineIndex) => {
480
490
  const lineSpan = createSpan(lineClass, lineIndex, "block", {
481
491
  propIndex: options == null ? void 0 : options.propIndex,
482
- willChange: options == null ? void 0 : options.willChange,
483
- propName: "line"
492
+ propName: "line",
493
+ ariaHidden: options == null ? void 0 : options.ariaHidden
484
494
  });
485
495
  allLines.push(lineSpan);
486
496
  let wi = 0;
@@ -575,7 +585,6 @@ function performSplit(element, measuredWords, charClass, wordClass, lineClass, s
575
585
  lineGroups.forEach((wrappers, lineIndex) => {
576
586
  const lineSpan = createSpan(lineClass, lineIndex, "block", {
577
587
  propIndex: options == null ? void 0 : options.propIndex,
578
- willChange: options == null ? void 0 : options.willChange,
579
588
  propName: "line"
580
589
  });
581
590
  allLines.push(lineSpan);
@@ -617,8 +626,7 @@ function splitText(element, {
617
626
  onResize,
618
627
  onSplit,
619
628
  revertOnComplete = false,
620
- propIndex = false,
621
- willChange = true
629
+ propIndex = false
622
630
  } = {}) {
623
631
  var _a;
624
632
  if (!(element instanceof HTMLElement)) {
@@ -673,7 +681,7 @@ function splitText(element, {
673
681
  splitChars,
674
682
  splitWords,
675
683
  splitLines,
676
- { propIndex, willChange, mask }
684
+ { propIndex, mask, ariaHidden: !trackAncestors }
677
685
  );
678
686
  currentChars = chars;
679
687
  currentWords = words;
@@ -751,7 +759,7 @@ function splitText(element, {
751
759
  splitChars,
752
760
  splitWords,
753
761
  splitLines,
754
- { propIndex, willChange, mask }
762
+ { propIndex, mask, ariaHidden: !trackAncestors }
755
763
  );
756
764
  currentChars = result.chars;
757
765
  currentWords = result.words;
package/dist/index.d.ts CHANGED
@@ -42,8 +42,6 @@ interface SplitTextOptions {
42
42
  revertOnComplete?: boolean;
43
43
  /** Add CSS custom properties (--char-index, --word-index, --line-index) */
44
44
  propIndex?: boolean;
45
- /** Add will-change: transform, opacity to split elements for better animation performance (default: true) */
46
- willChange?: boolean;
47
45
  }
48
46
  /**
49
47
  * Result returned by splitText containing arrays of split elements and a revert function.
@@ -110,6 +108,6 @@ interface SplitTextResult {
110
108
  * });
111
109
  * ```
112
110
  */
113
- declare function splitText(element: HTMLElement, { type, charClass, wordClass, lineClass, mask, autoSplit, onResize, onSplit, revertOnComplete, propIndex, willChange, }?: SplitTextOptions): SplitTextResult;
111
+ declare function splitText(element: HTMLElement, { type, charClass, wordClass, lineClass, mask, autoSplit, onResize, onSplit, revertOnComplete, propIndex, }?: SplitTextOptions): SplitTextResult;
114
112
 
115
113
  export { type SplitTextOptions, type SplitTextResult, splitText };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export { splitText } from './chunk-7L4UQPGS.js';
1
+ export { splitText } from './chunk-WGVCUEOU.js';
package/dist/react.d.ts CHANGED
@@ -10,7 +10,6 @@ interface SplitTextOptions {
10
10
  /** Apply overflow mask wrapper to elements for reveal animations */
11
11
  mask?: "lines" | "words" | "chars";
12
12
  propIndex?: boolean;
13
- willChange?: boolean;
14
13
  }
15
14
  interface InViewOptions {
16
15
  /** How much of the element must be visible (0-1). Default: 0 */
package/dist/react.js CHANGED
@@ -1,4 +1,4 @@
1
- import { splitText, __spreadProps, __spreadValues, normalizeToPromise } from './chunk-7L4UQPGS.js';
1
+ import { splitText, __spreadProps, __spreadValues, normalizeToPromise } from './chunk-WGVCUEOU.js';
2
2
  import { forwardRef, useRef, useCallback, useState, useLayoutEffect, useEffect, isValidElement, cloneElement } from 'react';
3
3
  import { jsx } from 'react/jsx-runtime';
4
4
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fetta",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "Text splitting library with kerning compensation for animations",
5
5
  "type": "module",
6
6
  "sideEffects": false,