fetta 1.0.2 → 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 +3 -3
- package/dist/{chunk-G5P33GJE.js → chunk-7L4UQPGS.js} +50 -2
- 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,30 @@ 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:not(:focus):not(:active) {
|
|
73
|
+
clip: rect(0 0 0 0);
|
|
74
|
+
clip-path: inset(50%);
|
|
75
|
+
height: 1px;
|
|
76
|
+
overflow: hidden;
|
|
77
|
+
position: absolute;
|
|
78
|
+
white-space: nowrap;
|
|
79
|
+
width: 1px;
|
|
80
|
+
}`;
|
|
81
|
+
document.head.appendChild(style);
|
|
82
|
+
srOnlyStylesInjected = true;
|
|
83
|
+
}
|
|
84
|
+
function createScreenReaderCopy(originalHTML) {
|
|
85
|
+
const srCopy = document.createElement("span");
|
|
86
|
+
srCopy.className = "fetta-sr-only";
|
|
87
|
+
srCopy.innerHTML = originalHTML;
|
|
88
|
+
srCopy.dataset.fettaSrCopy = "true";
|
|
89
|
+
return srCopy;
|
|
90
|
+
}
|
|
67
91
|
function hasInlineDescendants(element) {
|
|
68
92
|
const walker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT);
|
|
69
93
|
let node;
|
|
@@ -631,7 +655,6 @@ function splitText(element, {
|
|
|
631
655
|
let currentChars = [];
|
|
632
656
|
let currentWords = [];
|
|
633
657
|
let currentLines = [];
|
|
634
|
-
element.setAttribute("aria-label", text);
|
|
635
658
|
if (splitChars) {
|
|
636
659
|
element.style.fontVariantLigatures = "none";
|
|
637
660
|
}
|
|
@@ -655,6 +678,19 @@ function splitText(element, {
|
|
|
655
678
|
currentChars = chars;
|
|
656
679
|
currentWords = words;
|
|
657
680
|
currentLines = lines;
|
|
681
|
+
if (trackAncestors) {
|
|
682
|
+
injectSrOnlyStyles();
|
|
683
|
+
const visualWrapper = document.createElement("span");
|
|
684
|
+
visualWrapper.setAttribute("aria-hidden", "true");
|
|
685
|
+
visualWrapper.dataset.fettaVisual = "true";
|
|
686
|
+
while (element.firstChild) {
|
|
687
|
+
visualWrapper.appendChild(element.firstChild);
|
|
688
|
+
}
|
|
689
|
+
element.appendChild(visualWrapper);
|
|
690
|
+
element.appendChild(createScreenReaderCopy(originalHTML));
|
|
691
|
+
} else {
|
|
692
|
+
element.setAttribute("aria-label", text);
|
|
693
|
+
}
|
|
658
694
|
const dispose = () => {
|
|
659
695
|
if (!isActive) return;
|
|
660
696
|
if (resizeObserver) {
|
|
@@ -670,7 +706,9 @@ function splitText(element, {
|
|
|
670
706
|
const revert = () => {
|
|
671
707
|
if (!isActive) return;
|
|
672
708
|
element.innerHTML = originalHTML;
|
|
673
|
-
|
|
709
|
+
if (!trackAncestors) {
|
|
710
|
+
element.removeAttribute("aria-label");
|
|
711
|
+
}
|
|
674
712
|
if (splitChars) {
|
|
675
713
|
element.style.fontVariantLigatures = "none";
|
|
676
714
|
}
|
|
@@ -718,6 +756,16 @@ function splitText(element, {
|
|
|
718
756
|
currentChars = result.chars;
|
|
719
757
|
currentWords = result.words;
|
|
720
758
|
currentLines = result.lines;
|
|
759
|
+
if (trackAncestors) {
|
|
760
|
+
const visualWrapper = document.createElement("span");
|
|
761
|
+
visualWrapper.setAttribute("aria-hidden", "true");
|
|
762
|
+
visualWrapper.dataset.fettaVisual = "true";
|
|
763
|
+
while (element.firstChild) {
|
|
764
|
+
visualWrapper.appendChild(element.firstChild);
|
|
765
|
+
}
|
|
766
|
+
element.appendChild(visualWrapper);
|
|
767
|
+
element.appendChild(createScreenReaderCopy(originalHTML));
|
|
768
|
+
}
|
|
721
769
|
const newFingerprint = getLineFingerprint(result.lines);
|
|
722
770
|
if (onResize && newFingerprint !== previousFingerprint) {
|
|
723
771
|
onResize({
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { splitText } from './chunk-
|
|
1
|
+
export { splitText } from './chunk-7L4UQPGS.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-7L4UQPGS.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.0
|
|
3
|
+
"version": "1.1.0",
|
|
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"
|