animate-text-weight 0.1.0 → 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/LICENSE +21 -0
- package/README.md +216 -39
- package/dist/index.d.ts +7 -4
- package/dist/index.js +484 -72
- package/dist/wave-nav.d.ts +12 -0
- package/dist/wave-nav.js +96 -0
- package/dist/waves.d.ts +58 -0
- package/dist/waves.js +52 -0
- package/package.json +10 -2
- package/src/animateTextWeight.css +288 -10
- package/src/index.ts +506 -68
- package/src/wave-nav.ts +113 -0
- package/src/waves.ts +73 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tim Schoch
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,76 +1,253 @@
|
|
|
1
1
|
# animateTextWeight
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
The text can span one line or several, but it works best on short text.
|
|
5
|
-
Body text animates fine in Chrome, but jitters in Firefox.
|
|
3
|
+
Use this for headlines, buttons and navigations to create stunning typographic animations. It animates the font-weight of a variable font while keeping the overall width so things don't randomly shift around.
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
- The `from` state keeps your letter-spacing. The `to` state gets
|
|
9
|
-
spacing matched per line, so every line ends where it started.
|
|
5
|
+

|
|
10
6
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
pre-rendered, the browser measures at runtime.
|
|
7
|
+
A variable font gets wider as it gets bolder. If you animate `font-weight` on its own, each line grows as the weight goes up, and the text around it moves. animateTextWeight measures each rendered line at the start and end weight and 9 steps between. At each of them it holds the line at its start width: with letter-spacing in line mode, and with the width of each character box in character mode.
|
|
8
|
+
|
|
9
|
+
[Buy me a coffee](https://revolut.me/hopefamily/pocket/x3dTEpx4pk)
|
|
15
10
|
|
|
16
11
|
## Sandbox
|
|
17
12
|
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
Try it live: https://timschoch.github.io/animateTextWeight/demo/
|
|
14
|
+
|
|
15
|
+
Or run it locally:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
# http://localhost:3000/demo/
|
|
19
|
+
npm run demo
|
|
20
|
+
```
|
|
20
21
|
|
|
21
22
|
## Use
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
Install it from npm.
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
npm install animate-text-weight
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Add it to your website or web app.
|
|
24
31
|
|
|
25
32
|
```js
|
|
26
33
|
import "animate-text-weight/style.css";
|
|
27
34
|
import { init } from "animate-text-weight";
|
|
35
|
+
|
|
28
36
|
init();
|
|
37
|
+
|
|
38
|
+
// If your bundler does not handle CSS imports, link `node_modules/animate-text-weight/src/animateTextWeight.css` directly.
|
|
29
39
|
```
|
|
30
40
|
|
|
31
|
-
|
|
32
|
-
The element's own `font-weight` is the `from` weight:
|
|
41
|
+
Add your CSS styling.
|
|
33
42
|
|
|
34
43
|
```css
|
|
35
|
-
|
|
36
|
-
|
|
44
|
+
@import url("https://fonts.googleapis.com/css2?family=Outfit:wght@100..900&display=swap");
|
|
45
|
+
|
|
46
|
+
.nav a {
|
|
47
|
+
font-family: "Outfit", sans-serif; /* a variable font */
|
|
48
|
+
--animate-weight-from: 300;
|
|
37
49
|
--animate-weight-to: 700;
|
|
38
50
|
}
|
|
39
|
-
|
|
40
|
-
|
|
51
|
+
|
|
52
|
+
.nav a:hover {
|
|
41
53
|
--animate-weight: true;
|
|
42
54
|
}
|
|
43
55
|
```
|
|
44
56
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
57
|
+
The script then wraps each line in a span. On hover that span animates from 300 to 700 and keeps its width.
|
|
58
|
+
|
|
59
|
+
```html
|
|
60
|
+
<a href="/">Foobar</a>
|
|
49
61
|
|
|
50
|
-
|
|
62
|
+
<!-- becomes -->
|
|
63
|
+
<a href="/">
|
|
64
|
+
<span class="animate-text-weight-line animate-text-weight-unit">Foobar</span>
|
|
65
|
+
</a>
|
|
66
|
+
```
|
|
51
67
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
68
|
+
### CSS
|
|
69
|
+
|
|
70
|
+
Every available option:
|
|
55
71
|
|
|
56
72
|
```css
|
|
57
|
-
.
|
|
58
|
-
|
|
73
|
+
.nav a {
|
|
74
|
+
font-family: "Outfit", sans-serif;
|
|
75
|
+
--animate-weight-from: 300;
|
|
76
|
+
--animate-weight-to: 700;
|
|
77
|
+
--animate-weight-by: character;
|
|
78
|
+
--animate-weight-delay: 20ms;
|
|
79
|
+
--animate-weight-direction: pointer;
|
|
80
|
+
--animate-weight-duration: 300ms;
|
|
81
|
+
--animate-weight-easing: linear;
|
|
82
|
+
--animate-weight-color-from: #1c1c1c;
|
|
83
|
+
--animate-weight-color-to: #6f4e37;
|
|
59
84
|
}
|
|
60
85
|
```
|
|
61
86
|
|
|
62
|
-
|
|
63
|
-
the unit is the character, so each one carries its own progress and its own
|
|
64
|
-
delay, and the fade arrives as a wave:
|
|
87
|
+
Then turn the animation on with `--animate-weight: true`, on the element itself or on any ancestor:
|
|
65
88
|
|
|
66
89
|
```css
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
--animate-weight
|
|
70
|
-
|
|
71
|
-
|
|
90
|
+
/* animate all elements that have a --animate-weight-to */
|
|
91
|
+
.nav {
|
|
92
|
+
--animate-weight: true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* or turn on specific elements only */
|
|
96
|
+
.nav a:hover,
|
|
97
|
+
.nav a[aria-current="page"] {
|
|
98
|
+
--animate-weight: true;
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The animation is shown if `--animate-weight-to` is set and differs from its current weight (vanilla `font-weight` or `--animate-weight-from`).
|
|
103
|
+
|
|
104
|
+
Set it on the element that holds the text itself. In `<a><span>Home</span></a>`, set it on the `span`, not the `a`.
|
|
105
|
+
|
|
106
|
+
Plain `letter-spacing` works and is kept. So do `text-transform`, `text-align` (except `justify`), `font-variant`, `font-feature-settings`, right-to-left text and `vw` font sizes.
|
|
107
|
+
|
|
108
|
+
| Property | Value | Default | Required |
|
|
109
|
+
| --- | --- | --- | --- |
|
|
110
|
+
| `--animate-weight-from` | Weight at rest. | The element's `font-weight` | no |
|
|
111
|
+
| `--animate-weight-to` | Weight when on. | none | yes |
|
|
112
|
+
| `--animate-weight` | `true` or `false`. Inherited, so an ancestor can set it. | `false` | no |
|
|
113
|
+
| `--animate-weight-by` | `line` animates each line as one unit. `character` animates each character on its own. | `line` | no |
|
|
114
|
+
| `--animate-weight-delay` | How long each unit waits after the one before it. Use it with `character` for a wave across the text, or with `line` for a wave down a block. Start with 5-20ms by character, 100ms+ by line. | `0s` | no |
|
|
115
|
+
| `--animate-weight-direction` | Where the wave starts. See below. | `start` | no |
|
|
116
|
+
| `--animate-weight-duration` | How long one unit takes to go from one weight to the other. With `prefers-reduced-motion: reduce`, duration and delay are set to `0s` and the weight changes at once. | `300ms` | no |
|
|
117
|
+
| `--animate-weight-easing` | Timing function of the wave. | `linear` | no |
|
|
118
|
+
|
|
119
|
+
#### `--animate-weight-direction`
|
|
120
|
+
|
|
121
|
+
| Value | Wave starts at ... |
|
|
122
|
+
| --- | --- |
|
|
123
|
+
| `start` | the start of the text. In right-to-left text that is the right side. In line mode it is the first line. |
|
|
124
|
+
| `end` | the other end. |
|
|
125
|
+
| `pointer` | the side the pointer came in from, and the weight goes back from the side the pointer left by. The pointer only sets the direction. You still need `--animate-weight: true` on `:hover` or another state to turn it on. |
|
|
126
|
+
| `scroll` | the start when the page scrolls down, and the end when it scrolls up. |
|
|
127
|
+
| `pointer scroll` | whichever of the two moved last. |
|
|
128
|
+
|
|
129
|
+
#### Markup
|
|
130
|
+
|
|
131
|
+
The script splits the element into one span per line, and in character mode into one span per character. Inline styles are left out here:
|
|
132
|
+
|
|
133
|
+
```html
|
|
134
|
+
<!-- before -->
|
|
135
|
+
<a>Foobar</a>
|
|
136
|
+
|
|
137
|
+
<!-- by line -->
|
|
138
|
+
<a>
|
|
139
|
+
<span class="animate-text-weight-line animate-text-weight-unit">Foobar</span>
|
|
140
|
+
</a>
|
|
141
|
+
|
|
142
|
+
<!-- by character -->
|
|
143
|
+
<a>
|
|
144
|
+
<span class="animate-text-weight-line">
|
|
145
|
+
<span class="animate-text-weight-char animate-text-weight-unit">F</span>
|
|
146
|
+
<span class="animate-text-weight-char animate-text-weight-unit">o</span>
|
|
147
|
+
<span class="animate-text-weight-char animate-text-weight-unit">o</span>
|
|
148
|
+
...
|
|
149
|
+
</span>
|
|
150
|
+
</a>
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Lines are fixed after measuring. They can get wider or narrower but do not rewrap until the next measurement.
|
|
154
|
+
|
|
155
|
+
#### Known limitations
|
|
156
|
+
|
|
157
|
+
- Two weights only per element: from and to. A third state, like rest, hover and active, is not possible yet. PRs welcome.
|
|
158
|
+
- Needs a variable font. Static fonts only have fixed weights, so the text jumps from one weight to the next.
|
|
159
|
+
- Character mode drops ligatures.
|
|
160
|
+
- Line mode on long body text jitters in Firefox.
|
|
161
|
+
- `font-variation-settings` with `"wght"` overrides the weight, so nothing animates. Use `font-weight`.
|
|
162
|
+
- `text-align: justify` loses its stretch, because lines are fixed after measuring.
|
|
163
|
+
|
|
164
|
+
### JavaScript
|
|
165
|
+
|
|
166
|
+
#### On page load
|
|
167
|
+
|
|
168
|
+
This is enough for most pages. Call it once. It waits for the fonts, measures every animated element on the page, and measures them again when the window width changes.
|
|
169
|
+
|
|
170
|
+
```js
|
|
171
|
+
init();
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
#### Text added or changed after load
|
|
175
|
+
|
|
176
|
+
Measure the element again after you add it or replace its text.
|
|
177
|
+
|
|
178
|
+
```js
|
|
179
|
+
const link = document.createElement("a");
|
|
180
|
+
link.textContent = "Foobar";
|
|
181
|
+
nav.append(link);
|
|
182
|
+
calibrate(link);
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### New view in a single-page app
|
|
186
|
+
|
|
187
|
+
Measure everything under the new view's root. Those elements are measured again on resize too.
|
|
188
|
+
|
|
189
|
+
```js
|
|
190
|
+
calibrateAll(document.querySelector("main"));
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
#### Known limitations
|
|
194
|
+
|
|
195
|
+
- The script measures again only when the window width changes. After a new font size, a new container width, a new window height with `vh` font sizes or a font that loads late, call `calibrate(element)`.
|
|
196
|
+
|
|
197
|
+
## Extras
|
|
198
|
+
|
|
199
|
+
### Color
|
|
200
|
+
|
|
201
|
+
Set `--animate-weight-color-from` or `--animate-weight-color-to` to change color along with the weight. If you omit one or the other, it falls back to `currentColor` and uses whatever color your own CSS already applies to that text:
|
|
202
|
+
|
|
203
|
+
```css
|
|
204
|
+
.nav a {
|
|
205
|
+
--animate-weight-color-from: #1c1c1c;
|
|
206
|
+
--animate-weight-color-to: #c8102e;
|
|
72
207
|
}
|
|
73
208
|
```
|
|
74
209
|
|
|
75
|
-
|
|
76
|
-
|
|
210
|
+
The color follows each unit's weight, so it uses the same duration, easing, delay, and direction.
|
|
211
|
+
|
|
212
|
+
### Animate other properties with the weight animation
|
|
213
|
+
|
|
214
|
+
Each `span.animate-text-weight-unit` has the CSS variable `--animate-weight-progress`, which goes from `0` at `--animate-weight-from` to `1` at `--animate-weight-to`. Use it in any CSS property to sync that property to the weight animation:
|
|
215
|
+
|
|
216
|
+
```css
|
|
217
|
+
/* change opacity from 60% to 100% */
|
|
218
|
+
.nav a .animate-text-weight-unit {
|
|
219
|
+
opacity: calc(0.6 + 0.4 * var(--animate-weight-progress));
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
The CSS variable is set on the unit and is not inherited. Make sure to target `.animate-text-weight-unit` itself, not the original element (`.nav a`). In character mode each character has its own `span.animate-text-weight-unit` with its own value.
|
|
224
|
+
|
|
225
|
+
### Sticky one page navs
|
|
226
|
+
|
|
227
|
+
An optional extra marks the nav link of the section in the middle of the screen with `aria-current="true"`. A jump passes every section in between, one at a time, so one wave runs through the nav. Each step waits until the wave has crossed that link: `--animate-weight-delay` once per unit.
|
|
228
|
+
|
|
229
|
+
```js
|
|
230
|
+
import { waveNav } from "animate-text-weight/wave-nav";
|
|
231
|
+
|
|
232
|
+
waveNav(document.querySelectorAll(".nav a"));
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
```css
|
|
236
|
+
.nav a[aria-current="true"] {
|
|
237
|
+
--animate-weight: true;
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Each link's section is the element its `href="#id"` names. Pass `{ linkClass: "active" }` or `{ sectionClass: "active" }` to also toggle a class on the current link or its section. `waveNav` returns a `stop()` function that removes its listeners.
|
|
242
|
+
|
|
243
|
+
## Thanks
|
|
244
|
+
|
|
245
|
+
Thanks for using animateTextWeight.
|
|
246
|
+
|
|
247
|
+
It is free under the [MIT license](LICENSE): use it in any project, personal or commercial, change it and share it. Keep the license text with the code. It comes with no warranty.
|
|
248
|
+
|
|
249
|
+
Found a bug or want a feature? [Open an issue](https://github.com/timschoch/animateTextWeight/issues) or, better, send a pull request.
|
|
250
|
+
|
|
251
|
+
If you appreciate the typographic beauty of animated text that doesn't break your design, [buy me a coffee](https://revolut.me/hopefamily/pocket/x3dTEpx4pk).
|
|
252
|
+
|
|
253
|
+
I really enjoy seeing other designers' work and appreciate it if you [send me a link to any project that uses animateTextWeight](mailto:tim@timschoch.com).
|
package/dist/index.d.ts
CHANGED
|
@@ -41,7 +41,7 @@ export interface Report {
|
|
|
41
41
|
* The weight range set in CSS, or null when the element does not animate.
|
|
42
42
|
* --animate-weight-from is optional: the element's font-weight is the from weight.
|
|
43
43
|
*/
|
|
44
|
-
export declare function range(
|
|
44
|
+
export declare function range(element: Element): {
|
|
45
45
|
from: number;
|
|
46
46
|
to: number;
|
|
47
47
|
by: Mode;
|
|
@@ -49,9 +49,12 @@ export declare function range(el: Element): {
|
|
|
49
49
|
/** Elements under root that carry a weight range and hold text directly. */
|
|
50
50
|
export declare function findAll(root?: ParentNode): HTMLElement[];
|
|
51
51
|
/** Words of the live element grouped by rendered line. */
|
|
52
|
-
export declare function readLines(
|
|
53
|
-
/**
|
|
54
|
-
|
|
52
|
+
export declare function readLines(element: HTMLElement): string[];
|
|
53
|
+
/**
|
|
54
|
+
* Split one element into its lines, then its units, and write their values.
|
|
55
|
+
* Done again after each resize, for as long as the element is in the document.
|
|
56
|
+
*/
|
|
57
|
+
export declare function calibrate(element: HTMLElement): Report | null;
|
|
55
58
|
/** Calibrate every animated element under root, now and after each resize. */
|
|
56
59
|
export declare function calibrateAll(root?: ParentNode): HTMLElement[];
|
|
57
60
|
/** Wait for the fonts, then calibrate the page. */
|