react-animated-highlighter 1.0.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 ADDED
@@ -0,0 +1,123 @@
1
+ # React Animated Highlighter
2
+
3
+ An animated text highlighter component for React with smooth GSAP animations. Perfect for hero sections, landing pages, and attention-grabbing text effects.
4
+
5
+ ![Demo](./assets/demo.svg)
6
+
7
+ [![npm version](https://img.shields.io/npm/v/react-animated-highlighter)](https://www.npmjs.com/package/react-animated-highlighter)
8
+
9
+ ## Features
10
+
11
+ - Smooth GSAP-powered animations
12
+ - Highlighter icon that "draws" across your text
13
+ - Fully customizable colors, timing, and opacity
14
+ - Replay animation on hover option
15
+ - TypeScript support
16
+ - Zero external icon dependencies
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install react-animated-highlighter gsap
22
+ ```
23
+
24
+ ## Quick Start
25
+
26
+ ```tsx
27
+ import { AnimatedHighlighter } from 'react-animated-highlighter';
28
+
29
+ function App() {
30
+ return (
31
+ <h1>
32
+ Welcome to <AnimatedHighlighter text="My Website" />
33
+ </h1>
34
+ );
35
+ }
36
+ ```
37
+
38
+ ## Props
39
+
40
+ | Prop | Type | Default | Description |
41
+ |------|------|---------|-------------|
42
+ | `text` | `string` | **required** | The text to highlight |
43
+ | `color` | `string` | `'#6ca0dc'` | Highlight color |
44
+ | `fontSize` | `string` | `'30px'` | Font size of the text |
45
+ | `className` | `string` | `''` | Additional CSS class |
46
+ | `showIcon` | `boolean` | `true` | Show the highlighter icon during animation |
47
+ | `highlightOpacity` | `number` | `0.3` | Opacity of the highlight (0-1) |
48
+ | `duration` | `number` | `1.5` | Animation duration in seconds |
49
+ | `delay` | `number` | `0` | Delay before animation starts |
50
+ | `replayOnHover` | `boolean` | `false` | Replay the animation on hover |
51
+
52
+ ## Examples
53
+
54
+ ### Custom Color
55
+
56
+ ```tsx
57
+ <AnimatedHighlighter
58
+ text="Important!"
59
+ color="#f59e0b"
60
+ />
61
+ ```
62
+
63
+ ### Slower Animation
64
+
65
+ ```tsx
66
+ <AnimatedHighlighter
67
+ text="Take your time"
68
+ duration={3}
69
+ delay={0.5}
70
+ />
71
+ ```
72
+
73
+ ### No Icon, Just Highlight
74
+
75
+ ```tsx
76
+ <AnimatedHighlighter
77
+ text="Subtle highlight"
78
+ showIcon={false}
79
+ highlightOpacity={0.2}
80
+ />
81
+ ```
82
+
83
+ ### Interactive (Replay on Hover)
84
+
85
+ ```tsx
86
+ <AnimatedHighlighter
87
+ text="Hover me!"
88
+ replayOnHover={true}
89
+ />
90
+ ```
91
+
92
+ ### Hero Section Example
93
+
94
+ ```tsx
95
+ <h1 className="text-5xl font-bold">
96
+ Ship products with{' '}
97
+ <AnimatedHighlighter
98
+ text="zero friction"
99
+ color="#10b981"
100
+ fontSize="inherit"
101
+ duration={2}
102
+ />
103
+ </h1>
104
+ ```
105
+
106
+ ## Color Ideas
107
+
108
+ | Color | Hex | Use Case |
109
+ |-------|-----|----------|
110
+ | Blue | `#6ca0dc` | Default, professional |
111
+ | Emerald | `#10b981` | Success, growth |
112
+ | Amber | `#f59e0b` | Warning, attention |
113
+ | Rose | `#f43f5e` | Important, urgent |
114
+ | Purple | `#8b5cf6` | Creative, premium |
115
+
116
+ ## Requirements
117
+
118
+ - React 17+
119
+ - GSAP 3+ (peer dependency)
120
+
121
+ ## License
122
+
123
+ MIT © Ben Jammin
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+
3
+ interface AnimatedHighlighterProps {
4
+ /** The text to highlight */
5
+ text: string;
6
+ /** Highlight color (default: '#6ca0dc') */
7
+ color?: string;
8
+ /** Font size (default: '30px') */
9
+ fontSize?: string;
10
+ /** Additional CSS class name */
11
+ className?: string;
12
+ /** Whether to show the highlighter icon during animation (default: true) */
13
+ showIcon?: boolean;
14
+ /** Highlight opacity (default: 0.3) */
15
+ highlightOpacity?: number;
16
+ /** Animation duration in seconds (default: 1.5) */
17
+ duration?: number;
18
+ /** Delay before animation starts in seconds (default: 0) */
19
+ delay?: number;
20
+ /** Whether to replay animation on hover (default: false) */
21
+ replayOnHover?: boolean;
22
+ }
23
+
24
+ declare const AnimatedHighlighter: React.FC<AnimatedHighlighterProps>;
25
+
26
+ export { AnimatedHighlighter, type AnimatedHighlighterProps };
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+
3
+ interface AnimatedHighlighterProps {
4
+ /** The text to highlight */
5
+ text: string;
6
+ /** Highlight color (default: '#6ca0dc') */
7
+ color?: string;
8
+ /** Font size (default: '30px') */
9
+ fontSize?: string;
10
+ /** Additional CSS class name */
11
+ className?: string;
12
+ /** Whether to show the highlighter icon during animation (default: true) */
13
+ showIcon?: boolean;
14
+ /** Highlight opacity (default: 0.3) */
15
+ highlightOpacity?: number;
16
+ /** Animation duration in seconds (default: 1.5) */
17
+ duration?: number;
18
+ /** Delay before animation starts in seconds (default: 0) */
19
+ delay?: number;
20
+ /** Whether to replay animation on hover (default: false) */
21
+ replayOnHover?: boolean;
22
+ }
23
+
24
+ declare const AnimatedHighlighter: React.FC<AnimatedHighlighterProps>;
25
+
26
+ export { AnimatedHighlighter, type AnimatedHighlighterProps };
package/dist/index.js ADDED
@@ -0,0 +1,190 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ AnimatedHighlighter: () => AnimatedHighlighter
34
+ });
35
+ module.exports = __toCommonJS(index_exports);
36
+
37
+ // src/AnimatedHighlighter.tsx
38
+ var import_react = require("react");
39
+ var import_gsap = __toESM(require("gsap"));
40
+ var import_jsx_runtime = require("react/jsx-runtime");
41
+ var HighlighterIcon = ({ size, color }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
42
+ "svg",
43
+ {
44
+ xmlns: "http://www.w3.org/2000/svg",
45
+ width: size,
46
+ height: size,
47
+ viewBox: "0 0 24 24",
48
+ fill: "none",
49
+ stroke: color,
50
+ strokeWidth: "2",
51
+ strokeLinecap: "round",
52
+ strokeLinejoin: "round",
53
+ children: [
54
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m9 11-6 6v3h9l3-3" }),
55
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m22 12-4.6 4.6a2 2 0 0 1-2.8 0l-5.2-5.2a2 2 0 0 1 0-2.8L14 4" })
56
+ ]
57
+ }
58
+ );
59
+ var AnimatedHighlighter = ({
60
+ text,
61
+ color = "#6ca0dc",
62
+ fontSize = "30px",
63
+ className = "",
64
+ showIcon = true,
65
+ highlightOpacity = 0.3,
66
+ duration = 1.5,
67
+ delay = 0,
68
+ replayOnHover = false
69
+ }) => {
70
+ const containerRef = (0, import_react.useRef)(null);
71
+ const highlightRef = (0, import_react.useRef)(null);
72
+ const highlighterRef = (0, import_react.useRef)(null);
73
+ const timelineRef = (0, import_react.useRef)(null);
74
+ const runAnimation = (0, import_react.useCallback)(() => {
75
+ if (!containerRef.current || !highlightRef.current) return;
76
+ const highlight = highlightRef.current;
77
+ const highlighter = highlighterRef.current;
78
+ if (timelineRef.current) {
79
+ timelineRef.current.kill();
80
+ }
81
+ import_gsap.default.set(highlight, {
82
+ scaleX: 0,
83
+ transformOrigin: "left"
84
+ });
85
+ if (showIcon && highlighter) {
86
+ import_gsap.default.set(highlighter, {
87
+ opacity: 0,
88
+ scale: 0,
89
+ rotation: -30,
90
+ x: 0
91
+ });
92
+ }
93
+ const tl = import_gsap.default.timeline({ defaults: { ease: "power2.inOut" }, delay });
94
+ timelineRef.current = tl;
95
+ if (showIcon && highlighter) {
96
+ tl.to(highlighter, {
97
+ opacity: 1,
98
+ scale: 1,
99
+ duration: 0.3
100
+ }).to(highlight, {
101
+ scaleX: 1,
102
+ duration
103
+ }).to(
104
+ highlighter,
105
+ {
106
+ x: containerRef.current.offsetWidth,
107
+ duration
108
+ },
109
+ "<"
110
+ ).to(
111
+ highlighter,
112
+ {
113
+ opacity: 0,
114
+ scale: 0,
115
+ duration: 0.3
116
+ },
117
+ ">-0.3"
118
+ );
119
+ } else {
120
+ tl.to(highlight, {
121
+ scaleX: 1,
122
+ duration
123
+ });
124
+ }
125
+ }, [showIcon, duration, delay]);
126
+ (0, import_react.useEffect)(() => {
127
+ runAnimation();
128
+ return () => {
129
+ if (timelineRef.current) {
130
+ timelineRef.current.kill();
131
+ }
132
+ };
133
+ }, [runAnimation]);
134
+ const handleMouseEnter = () => {
135
+ if (replayOnHover) {
136
+ runAnimation();
137
+ }
138
+ };
139
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
140
+ "div",
141
+ {
142
+ ref: containerRef,
143
+ className,
144
+ style: {
145
+ position: "relative",
146
+ display: "inline-block",
147
+ fontSize
148
+ },
149
+ onMouseEnter: handleMouseEnter,
150
+ children: [
151
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { position: "relative" }, children: [
152
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
153
+ "div",
154
+ {
155
+ ref: highlightRef,
156
+ style: {
157
+ position: "absolute",
158
+ inset: 0,
159
+ backgroundColor: color,
160
+ opacity: highlightOpacity,
161
+ height: "100%",
162
+ width: "100%",
163
+ transform: "skewY(-2deg)"
164
+ }
165
+ }
166
+ ),
167
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { position: "relative" }, children: text })
168
+ ] }),
169
+ showIcon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
170
+ "div",
171
+ {
172
+ ref: highlighterRef,
173
+ style: {
174
+ position: "absolute",
175
+ left: 0,
176
+ top: "50%",
177
+ pointerEvents: "none",
178
+ color
179
+ },
180
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { transform: "translateY(-50%)" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HighlighterIcon, { size: 32, color }) })
181
+ }
182
+ )
183
+ ]
184
+ }
185
+ );
186
+ };
187
+ // Annotate the CommonJS export names for ESM import in node:
188
+ 0 && (module.exports = {
189
+ AnimatedHighlighter
190
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,153 @@
1
+ // src/AnimatedHighlighter.tsx
2
+ import { useEffect, useRef, useCallback } from "react";
3
+ import gsap from "gsap";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ var HighlighterIcon = ({ size, color }) => /* @__PURE__ */ jsxs(
6
+ "svg",
7
+ {
8
+ xmlns: "http://www.w3.org/2000/svg",
9
+ width: size,
10
+ height: size,
11
+ viewBox: "0 0 24 24",
12
+ fill: "none",
13
+ stroke: color,
14
+ strokeWidth: "2",
15
+ strokeLinecap: "round",
16
+ strokeLinejoin: "round",
17
+ children: [
18
+ /* @__PURE__ */ jsx("path", { d: "m9 11-6 6v3h9l3-3" }),
19
+ /* @__PURE__ */ jsx("path", { d: "m22 12-4.6 4.6a2 2 0 0 1-2.8 0l-5.2-5.2a2 2 0 0 1 0-2.8L14 4" })
20
+ ]
21
+ }
22
+ );
23
+ var AnimatedHighlighter = ({
24
+ text,
25
+ color = "#6ca0dc",
26
+ fontSize = "30px",
27
+ className = "",
28
+ showIcon = true,
29
+ highlightOpacity = 0.3,
30
+ duration = 1.5,
31
+ delay = 0,
32
+ replayOnHover = false
33
+ }) => {
34
+ const containerRef = useRef(null);
35
+ const highlightRef = useRef(null);
36
+ const highlighterRef = useRef(null);
37
+ const timelineRef = useRef(null);
38
+ const runAnimation = useCallback(() => {
39
+ if (!containerRef.current || !highlightRef.current) return;
40
+ const highlight = highlightRef.current;
41
+ const highlighter = highlighterRef.current;
42
+ if (timelineRef.current) {
43
+ timelineRef.current.kill();
44
+ }
45
+ gsap.set(highlight, {
46
+ scaleX: 0,
47
+ transformOrigin: "left"
48
+ });
49
+ if (showIcon && highlighter) {
50
+ gsap.set(highlighter, {
51
+ opacity: 0,
52
+ scale: 0,
53
+ rotation: -30,
54
+ x: 0
55
+ });
56
+ }
57
+ const tl = gsap.timeline({ defaults: { ease: "power2.inOut" }, delay });
58
+ timelineRef.current = tl;
59
+ if (showIcon && highlighter) {
60
+ tl.to(highlighter, {
61
+ opacity: 1,
62
+ scale: 1,
63
+ duration: 0.3
64
+ }).to(highlight, {
65
+ scaleX: 1,
66
+ duration
67
+ }).to(
68
+ highlighter,
69
+ {
70
+ x: containerRef.current.offsetWidth,
71
+ duration
72
+ },
73
+ "<"
74
+ ).to(
75
+ highlighter,
76
+ {
77
+ opacity: 0,
78
+ scale: 0,
79
+ duration: 0.3
80
+ },
81
+ ">-0.3"
82
+ );
83
+ } else {
84
+ tl.to(highlight, {
85
+ scaleX: 1,
86
+ duration
87
+ });
88
+ }
89
+ }, [showIcon, duration, delay]);
90
+ useEffect(() => {
91
+ runAnimation();
92
+ return () => {
93
+ if (timelineRef.current) {
94
+ timelineRef.current.kill();
95
+ }
96
+ };
97
+ }, [runAnimation]);
98
+ const handleMouseEnter = () => {
99
+ if (replayOnHover) {
100
+ runAnimation();
101
+ }
102
+ };
103
+ return /* @__PURE__ */ jsxs(
104
+ "div",
105
+ {
106
+ ref: containerRef,
107
+ className,
108
+ style: {
109
+ position: "relative",
110
+ display: "inline-block",
111
+ fontSize
112
+ },
113
+ onMouseEnter: handleMouseEnter,
114
+ children: [
115
+ /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
116
+ /* @__PURE__ */ jsx(
117
+ "div",
118
+ {
119
+ ref: highlightRef,
120
+ style: {
121
+ position: "absolute",
122
+ inset: 0,
123
+ backgroundColor: color,
124
+ opacity: highlightOpacity,
125
+ height: "100%",
126
+ width: "100%",
127
+ transform: "skewY(-2deg)"
128
+ }
129
+ }
130
+ ),
131
+ /* @__PURE__ */ jsx("span", { style: { position: "relative" }, children: text })
132
+ ] }),
133
+ showIcon && /* @__PURE__ */ jsx(
134
+ "div",
135
+ {
136
+ ref: highlighterRef,
137
+ style: {
138
+ position: "absolute",
139
+ left: 0,
140
+ top: "50%",
141
+ pointerEvents: "none",
142
+ color
143
+ },
144
+ children: /* @__PURE__ */ jsx("div", { style: { transform: "translateY(-50%)" }, children: /* @__PURE__ */ jsx(HighlighterIcon, { size: 32, color }) })
145
+ }
146
+ )
147
+ ]
148
+ }
149
+ );
150
+ };
151
+ export {
152
+ AnimatedHighlighter
153
+ };
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "react-animated-highlighter",
3
+ "version": "1.0.0",
4
+ "description": "Animated text highlighter component for React with GSAP animations",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "scripts": {
12
+ "build": "tsup src/index.ts --format cjs,esm --dts",
13
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch"
14
+ },
15
+ "peerDependencies": {
16
+ "react": ">=17.0.0",
17
+ "react-dom": ">=17.0.0",
18
+ "gsap": ">=3.0.0"
19
+ },
20
+ "devDependencies": {
21
+ "@types/react": "^18.2.0",
22
+ "react": "^18.2.0",
23
+ "gsap": "^3.12.0",
24
+ "tsup": "^8.0.0",
25
+ "typescript": "^5.0.0"
26
+ },
27
+ "keywords": [
28
+ "react",
29
+ "animation",
30
+ "highlighter",
31
+ "gsap",
32
+ "text-animation",
33
+ "underline",
34
+ "highlight",
35
+ "component"
36
+ ],
37
+ "author": "Ben Jammin",
38
+ "license": "MIT",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/xBenJamminx/react-animated-highlighter"
42
+ }
43
+ }