fetta 1.0.2 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/{chunk-G5P33GJE.js → chunk-6EZFFMJ3.js} +63 -7
- package/dist/index.js +1 -1
- package/dist/react.js +1 -1
- package/package.json +19 -2
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Fetta
|
|
2
2
|
|
|
3
3
|
A text-splitting library with kerning compensation for smooth, natural text animations.
|
|
4
4
|
|
|
@@ -14,7 +14,7 @@ Split text into characters, words, and lines while preserving the original typog
|
|
|
14
14
|
- **Auto-Revert** — Restore original HTML after animations
|
|
15
15
|
- **Masking** — Wrap elements in clip containers for reveal animations
|
|
16
16
|
- **Emoji Support** — Properly handles compound emojis and complex Unicode characters
|
|
17
|
-
- **Accessible** —
|
|
17
|
+
- **Accessible** — Automatic screen reader support, even when splitting text with nested links or emphasis
|
|
18
18
|
- **TypeScript** — Full type definitions included
|
|
19
19
|
- **React Component** — Declarative wrapper for React projects
|
|
20
20
|
- **Built-in InView** — Viewport detection for scroll-triggered animations in React
|
|
@@ -255,7 +255,7 @@ Each element also receives a `data-index` attribute with its position.
|
|
|
255
255
|
|
|
256
256
|
- **Fonts must be loaded** before splitting. The React component waits for `document.fonts.ready` automatically.
|
|
257
257
|
- **Ligatures are disabled** (`font-variant-ligatures: none`) because ligatures cannot span multiple elements.
|
|
258
|
-
- **Accessibility**:
|
|
258
|
+
- **Accessibility**: Automatic screen reader support for both simple text and text with nested elements like links.
|
|
259
259
|
|
|
260
260
|
## Browser Support
|
|
261
261
|
|
|
@@ -64,6 +64,32 @@ var INLINE_ELEMENTS = /* @__PURE__ */ new Set([
|
|
|
64
64
|
"var"
|
|
65
65
|
]);
|
|
66
66
|
var isSafari = typeof navigator !== "undefined" && /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
|
|
67
|
+
var srOnlyStylesInjected = false;
|
|
68
|
+
function injectSrOnlyStyles() {
|
|
69
|
+
if (srOnlyStylesInjected || typeof document === "undefined") return;
|
|
70
|
+
const style = document.createElement("style");
|
|
71
|
+
style.textContent = `
|
|
72
|
+
.fetta-sr-only {
|
|
73
|
+
position: absolute;
|
|
74
|
+
width: 1px;
|
|
75
|
+
height: 1px;
|
|
76
|
+
padding: 0;
|
|
77
|
+
margin: -1px;
|
|
78
|
+
overflow: hidden;
|
|
79
|
+
clip-path: inset(50%);
|
|
80
|
+
white-space: nowrap;
|
|
81
|
+
border-width: 0;
|
|
82
|
+
}`;
|
|
83
|
+
document.head.appendChild(style);
|
|
84
|
+
srOnlyStylesInjected = true;
|
|
85
|
+
}
|
|
86
|
+
function createScreenReaderCopy(originalHTML) {
|
|
87
|
+
const srCopy = document.createElement("span");
|
|
88
|
+
srCopy.className = "fetta-sr-only";
|
|
89
|
+
srCopy.innerHTML = originalHTML;
|
|
90
|
+
srCopy.dataset.fettaSrCopy = "true";
|
|
91
|
+
return srCopy;
|
|
92
|
+
}
|
|
67
93
|
function hasInlineDescendants(element) {
|
|
68
94
|
const walker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT);
|
|
69
95
|
let node;
|
|
@@ -236,6 +262,9 @@ function createSpan(className, index, display = "inline-block", options) {
|
|
|
236
262
|
if (options == null ? void 0 : options.willChange) {
|
|
237
263
|
span.style.willChange = "transform, opacity";
|
|
238
264
|
}
|
|
265
|
+
if (options == null ? void 0 : options.ariaHidden) {
|
|
266
|
+
span.setAttribute("aria-hidden", "true");
|
|
267
|
+
}
|
|
239
268
|
return span;
|
|
240
269
|
}
|
|
241
270
|
function createMaskWrapper(display = "inline-block") {
|
|
@@ -283,7 +312,8 @@ function performSplit(element, measuredWords, charClass, wordClass, lineClass, s
|
|
|
283
312
|
const wordSpan = createSpan(wordClass, wordIndex, "inline-block", {
|
|
284
313
|
propIndex: options == null ? void 0 : options.propIndex,
|
|
285
314
|
willChange: options == null ? void 0 : options.willChange,
|
|
286
|
-
propName: "word"
|
|
315
|
+
propName: "word",
|
|
316
|
+
ariaHidden: options == null ? void 0 : options.ariaHidden
|
|
287
317
|
});
|
|
288
318
|
if (measuredWord.noSpaceBefore) {
|
|
289
319
|
noSpaceBeforeSet.add(wordSpan);
|
|
@@ -295,7 +325,8 @@ function performSplit(element, measuredWords, charClass, wordClass, lineClass, s
|
|
|
295
325
|
const charSpan = createSpan(charClass, globalCharIndex, "inline-block", {
|
|
296
326
|
propIndex: options == null ? void 0 : options.propIndex,
|
|
297
327
|
willChange: options == null ? void 0 : options.willChange,
|
|
298
|
-
propName: "char"
|
|
328
|
+
propName: "char",
|
|
329
|
+
ariaHidden: options == null ? void 0 : options.ariaHidden
|
|
299
330
|
});
|
|
300
331
|
charSpan.textContent = measuredChar.char;
|
|
301
332
|
globalCharIndex++;
|
|
@@ -456,7 +487,8 @@ function performSplit(element, measuredWords, charClass, wordClass, lineClass, s
|
|
|
456
487
|
const lineSpan = createSpan(lineClass, lineIndex, "block", {
|
|
457
488
|
propIndex: options == null ? void 0 : options.propIndex,
|
|
458
489
|
willChange: options == null ? void 0 : options.willChange,
|
|
459
|
-
propName: "line"
|
|
490
|
+
propName: "line",
|
|
491
|
+
ariaHidden: options == null ? void 0 : options.ariaHidden
|
|
460
492
|
});
|
|
461
493
|
allLines.push(lineSpan);
|
|
462
494
|
let wi = 0;
|
|
@@ -631,7 +663,6 @@ function splitText(element, {
|
|
|
631
663
|
let currentChars = [];
|
|
632
664
|
let currentWords = [];
|
|
633
665
|
let currentLines = [];
|
|
634
|
-
element.setAttribute("aria-label", text);
|
|
635
666
|
if (splitChars) {
|
|
636
667
|
element.style.fontVariantLigatures = "none";
|
|
637
668
|
}
|
|
@@ -650,11 +681,24 @@ function splitText(element, {
|
|
|
650
681
|
splitChars,
|
|
651
682
|
splitWords,
|
|
652
683
|
splitLines,
|
|
653
|
-
{ propIndex, willChange, mask }
|
|
684
|
+
{ propIndex, willChange, mask, ariaHidden: !trackAncestors }
|
|
654
685
|
);
|
|
655
686
|
currentChars = chars;
|
|
656
687
|
currentWords = words;
|
|
657
688
|
currentLines = lines;
|
|
689
|
+
if (trackAncestors) {
|
|
690
|
+
injectSrOnlyStyles();
|
|
691
|
+
const visualWrapper = document.createElement("span");
|
|
692
|
+
visualWrapper.setAttribute("aria-hidden", "true");
|
|
693
|
+
visualWrapper.dataset.fettaVisual = "true";
|
|
694
|
+
while (element.firstChild) {
|
|
695
|
+
visualWrapper.appendChild(element.firstChild);
|
|
696
|
+
}
|
|
697
|
+
element.appendChild(visualWrapper);
|
|
698
|
+
element.appendChild(createScreenReaderCopy(originalHTML));
|
|
699
|
+
} else {
|
|
700
|
+
element.setAttribute("aria-label", text);
|
|
701
|
+
}
|
|
658
702
|
const dispose = () => {
|
|
659
703
|
if (!isActive) return;
|
|
660
704
|
if (resizeObserver) {
|
|
@@ -670,7 +714,9 @@ function splitText(element, {
|
|
|
670
714
|
const revert = () => {
|
|
671
715
|
if (!isActive) return;
|
|
672
716
|
element.innerHTML = originalHTML;
|
|
673
|
-
|
|
717
|
+
if (!trackAncestors) {
|
|
718
|
+
element.removeAttribute("aria-label");
|
|
719
|
+
}
|
|
674
720
|
if (splitChars) {
|
|
675
721
|
element.style.fontVariantLigatures = "none";
|
|
676
722
|
}
|
|
@@ -713,11 +759,21 @@ function splitText(element, {
|
|
|
713
759
|
splitChars,
|
|
714
760
|
splitWords,
|
|
715
761
|
splitLines,
|
|
716
|
-
{ propIndex, willChange, mask }
|
|
762
|
+
{ propIndex, willChange, mask, ariaHidden: !trackAncestors }
|
|
717
763
|
);
|
|
718
764
|
currentChars = result.chars;
|
|
719
765
|
currentWords = result.words;
|
|
720
766
|
currentLines = result.lines;
|
|
767
|
+
if (trackAncestors) {
|
|
768
|
+
const visualWrapper = document.createElement("span");
|
|
769
|
+
visualWrapper.setAttribute("aria-hidden", "true");
|
|
770
|
+
visualWrapper.dataset.fettaVisual = "true";
|
|
771
|
+
while (element.firstChild) {
|
|
772
|
+
visualWrapper.appendChild(element.firstChild);
|
|
773
|
+
}
|
|
774
|
+
element.appendChild(visualWrapper);
|
|
775
|
+
element.appendChild(createScreenReaderCopy(originalHTML));
|
|
776
|
+
}
|
|
721
777
|
const newFingerprint = getLineFingerprint(result.lines);
|
|
722
778
|
if (onResize && newFingerprint !== previousFingerprint) {
|
|
723
779
|
onResize({
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { splitText } from './chunk-
|
|
1
|
+
export { splitText } from './chunk-6EZFFMJ3.js';
|
package/dist/react.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { splitText, __spreadProps, __spreadValues, normalizeToPromise } from './chunk-
|
|
1
|
+
import { splitText, __spreadProps, __spreadValues, normalizeToPromise } from './chunk-6EZFFMJ3.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.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Text splitting library with kerning compensation for animations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -34,8 +34,23 @@
|
|
|
34
34
|
"test:ui": "vitest --ui",
|
|
35
35
|
"test:coverage": "vitest run --coverage",
|
|
36
36
|
"test:e2e": "playwright test",
|
|
37
|
-
"test:all": "vitest run --coverage && playwright test"
|
|
37
|
+
"test:all": "vitest run --coverage && playwright test",
|
|
38
|
+
"size": "size-limit",
|
|
39
|
+
"size:why": "size-limit --why"
|
|
38
40
|
},
|
|
41
|
+
"size-limit": [
|
|
42
|
+
{
|
|
43
|
+
"name": "Core",
|
|
44
|
+
"path": "dist/index.js",
|
|
45
|
+
"import": "*"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"name": "React",
|
|
49
|
+
"path": "dist/react.js",
|
|
50
|
+
"import": "*",
|
|
51
|
+
"ignore": ["react"]
|
|
52
|
+
}
|
|
53
|
+
],
|
|
39
54
|
"peerDependencies": {
|
|
40
55
|
"react": ">=18.0.0"
|
|
41
56
|
},
|
|
@@ -46,6 +61,7 @@
|
|
|
46
61
|
},
|
|
47
62
|
"devDependencies": {
|
|
48
63
|
"@playwright/test": "^1.49.0",
|
|
64
|
+
"@size-limit/preset-small-lib": "^12.0.0",
|
|
49
65
|
"@testing-library/dom": "^10.4.0",
|
|
50
66
|
"@testing-library/jest-dom": "^6.6.3",
|
|
51
67
|
"@testing-library/react": "^16.1.0",
|
|
@@ -56,6 +72,7 @@
|
|
|
56
72
|
"react": "^19.2.3",
|
|
57
73
|
"react-dom": "^19.0.0",
|
|
58
74
|
"serve": "^14.2.4",
|
|
75
|
+
"size-limit": "^12.0.0",
|
|
59
76
|
"tsup": "^8.0.0",
|
|
60
77
|
"typescript": "^5",
|
|
61
78
|
"vitest": "^2.1.8"
|