@profpowell/blink-wc 0.0.0-stage → 1.5.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Prof Thomas Powell
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,3 +1,321 @@
1
- # Temporary Holding Version
1
+ # <blink-wc>
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The `<blink>` tag reborn for 2026. A modern, accessible, themeable web component
4
+ for blinking content — from a buzzing neon sign to a green CRT terminal, a
5
+ flashing alert, a string blinked out in Morse code, or (yes) an annoying retro
6
+ web effect.
7
+
8
+ **[Live demo &amp; docs →](https://profpowell.github.io/blink-wc/)** — open the theme
9
+ picker and watch the themed blinks re-skin with Vanilla Breeze in real time.
10
+
11
+ > **A note on accessibility.** Blinking and flashing content is a genuine
12
+ > accessibility hazard (and the original `<blink>` was deprecated for good
13
+ > reason). `<blink-wc>` takes this seriously: it **stops entirely** under
14
+ > `prefers-reduced-motion`, is always pausable, and pauses when off-screen or in
15
+ > a hidden tab. Use it tastefully — and never for fast, full-screen strobing.
16
+
17
+ ## Features
18
+
19
+ - Zero dependencies — pure vanilla JavaScript, light DOM
20
+ - CSS-driven blink with three behaviors (`blink`, `pulse`, `flicker`)
21
+ - Configurable `rate`, `min-opacity`, and `count` (finite blinks then stop)
22
+ - Surface themes and per-unit motion modes (see Modes below)
23
+ - A `morse` mode that blinks the text out in International Morse code
24
+ - Pauses when offscreen (IntersectionObserver) and when the tab is hidden (Page Visibility)
25
+ - Respects `prefers-reduced-motion` — stops, and shows the text fully
26
+ - Legacy-compatible API: `start()`, `stop()`, `toggle()`
27
+ - Custom events: `blink-start`, `blink-pause`, `blink-cycle`
28
+ - Built-in themes and full CSS custom property theming
29
+ - Vanilla Breeze design-token aware
30
+ - TypeScript definitions included
31
+
32
+ ## Installation
33
+
34
+ ```bash
35
+ npm install @profpowell/blink-wc
36
+ ```
37
+
38
+ Or use via CDN:
39
+
40
+ ```html
41
+ <link rel="stylesheet" href="https://unpkg.com/@profpowell/blink-wc/dist/blink-wc.css" />
42
+ <script type="module" src="https://unpkg.com/@profpowell/blink-wc/dist/blink-wc.js"></script>
43
+ ```
44
+
45
+ The component renders in the light DOM, so its stylesheet must be present on the
46
+ page (link the CSS, or `import '@profpowell/blink-wc/style.css'` in a bundler).
47
+
48
+ ## Usage
49
+
50
+ ```html
51
+ <link rel="stylesheet" href="dist/blink-wc.css" />
52
+ <script type="module" src="dist/blink-wc.js"></script>
53
+
54
+ <blink-wc>Now you see me…</blink-wc>
55
+ ```
56
+
57
+ ## Attributes
58
+
59
+ | Attribute | Type | Default | Description |
60
+ | ---------------- | ------- | ---------- | ------------------------------------------------------------------ |
61
+ | `rate` | time | `1s` | CSS time for one blink cycle |
62
+ | `behavior` | string | `blink` | `blink` \| `pulse` \| `flicker` \| `steps` |
63
+ | `min-opacity` | number | per-mode | Opacity of the "off" phase (0–1) |
64
+ | `count` | number | `infinite` | Number of blinks, then stop |
65
+ | `steps` | number | `2` | Step count for the multi-step engine (min 2) |
66
+ | `step-durations` | list | equal | Per-step hold weights, e.g. `3 1 1` |
67
+ | `step-by` | string | `element` | `element` \| `letter` \| `word` — step the whole text or each unit |
68
+ | `pause-on-hover` | boolean | — | Also pauses on `:focus-within` |
69
+ | `play-state` | string | `running` | `running` \| `paused` |
70
+ | `reduced-motion` | string | `respect` | `respect` \| `ignore` |
71
+ | `mode` | string | — | A visual/motion preset (see Modes below) |
72
+ | `unit` | string | `letter` | `letter` \| `word` — granularity for the motion modes |
73
+
74
+ ## Modes
75
+
76
+ The `mode` attribute selects a preset. **Surface themes** style the container;
77
+ **per-unit motion modes** animate each letter (or word, with `unit="word"`); and
78
+ `morse` blinks the text out as code.
79
+
80
+ ### Surface themes
81
+
82
+ | Mode | Description |
83
+ | ---------- | -------------------------------------------------------- |
84
+ | `neon` | A buzzing neon sign that flickers on with a colored glow |
85
+ | `crt` | Green phosphor terminal glow over faint scanlines |
86
+ | `terminal` | Steady text with a blinking block cursor |
87
+ | `warning` | Amber hazard panel that blinks for attention |
88
+ | `alert` | A flashing red error banner |
89
+ | `police` | Alternating red/blue light bar |
90
+ | `vegas` | A glowing gold marquee sign |
91
+
92
+ ### Per-unit motion modes
93
+
94
+ These split the text into per-unit spans and animate each one with a staggered
95
+ delay, so the effect ripples across the text. With `unit="word"` the units are
96
+ whole words. They honor `prefers-reduced-motion`.
97
+
98
+ | Mode | Description |
99
+ | ------------ | -------------------------------------------------------------- |
100
+ | `wave` | A hard blink that travels across the units, one after another |
101
+ | `twinkle` | Every unit blinks on its own random clock, like stars |
102
+ | `sparkle` | Twinkle with a glinting scale + glow |
103
+ | `rainbow` | Each unit a hue of the spectrum, blinking as the colors cycle |
104
+ | `chase` | A single lit unit sweeps along, theater-marquee style |
105
+ | `glitch` | Units blink while jittering with a chromatic-aberration shadow |
106
+ | `cascade` | A soft fade pulse rippling unit-to-unit |
107
+ | `typewriter` | Units appear one by one, with a blinking caret at the end |
108
+ | `zoom` | Each letter swells toward you and fades, in a staggered ripple |
109
+ | `flip3d` | Split-flap board — each letter tumbles a full turn on X |
110
+ | `swing` | Letters dangle and swing like pendulums |
111
+ | `shake` | A nervous jitter with a blink, slightly out of phase |
112
+ | `heartbeat` | A double-thump scale pulse rippling across the letters |
113
+
114
+ ### Morse &amp; decode
115
+
116
+ These are JS-driven (a small `requestAnimationFrame` loop); both freeze when
117
+ paused/off-screen/tab-hidden and fall back to plain text under reduced motion.
118
+
119
+ | Mode | Description |
120
+ | -------- | ------------------------------------------------------------------------ |
121
+ | `morse` | Blinks the text out as International Morse code, with a dot/dash caption |
122
+ | `decode` | Units flicker through random glyphs, then lock onto the real text |
123
+
124
+ ### Step modes
125
+
126
+ These ride the multi-step engine (see below). Each step of the cycle is its own
127
+ style state, and `--blink-step-ease` morphs smoothly between them.
128
+
129
+ | Mode | Description |
130
+ | ---------- | ------------------------------------------------------------- |
131
+ | `extrude` | An isometric 3D block that punches toward you on-beat |
132
+ | `collapse` | The text squashes flat, then pops back up |
133
+ | `outline` | Alternates between a solid fill and a hollow outline |
134
+ | `morph` | A 4-step showcase — each step its own colour and transform |
135
+ | `depth` | A true-3D `translateZ` punch toward the viewer |
136
+ | `revolve` | A 4-step turn on the Y axis that flips to the back and around |
137
+
138
+ ## The multi-step engine
139
+
140
+ A blink doesn't have to be a single on/off toggle. With `behavior="steps"` (or
141
+ any step mode above), the component advances through `N` discrete **steps**
142
+ across one `rate` cycle, setting `data-step="0"`…`data-step="N-1"` in turn. Each
143
+ step is just a CSS state, so it can change colour, transform, outline, glow —
144
+ anything. A plain blink is simply the 2-step case.
145
+
146
+ ```html
147
+ <link rel="stylesheet" href="blink-wc.css" />
148
+ <script type="module" src="blink-wc.js"></script>
149
+
150
+ <style>
151
+ /* a custom 3-step traffic light */
152
+ blink-wc.light {
153
+ --blink-step-ease: 0.15s;
154
+ }
155
+ blink-wc.light :is(.blink-content, .blink-char)[data-step='0'] {
156
+ color: oklch(62% 0.2 25);
157
+ }
158
+ blink-wc.light :is(.blink-content, .blink-char)[data-step='1'] {
159
+ color: oklch(80% 0.16 85);
160
+ }
161
+ blink-wc.light :is(.blink-content, .blink-char)[data-step='2'] {
162
+ color: oklch(70% 0.18 145);
163
+ }
164
+ </style>
165
+
166
+ <!-- green holds 6×, yellow 1×, red 4× of the 11s cycle -->
167
+ <blink-wc class="light" behavior="steps" steps="3" step-durations="6 1 4" rate="11s">●</blink-wc>
168
+ ```
169
+
170
+ ### Timing per step
171
+
172
+ - **`step-durations`** sets how long each step _holds_, as a list of weights
173
+ (`"6 1 4"`); they're normalized across the `rate` cycle. Omit it for equal
174
+ slices. The list is cycled/truncated to fit the step count.
175
+ - **`--blink-step-ease`** is the transition _duration_ between steps: `0s`
176
+ (default) is a hard cut — a true blink; non-zero morphs smoothly.
177
+ - **`--blink-step-timing`** is the transition _timing-function_ (default `ease`).
178
+
179
+ `--blink-step-ease` and `--blink-step-timing` are read from the **incoming**
180
+ step's computed style, so each step can carry its own duration and easing. The
181
+ stepped element carries `data-step` (the `.blink-content`, or each `.blink-char`
182
+ when stepping per unit), so target your steps with
183
+ `blink-wc :is(.blink-content, .blink-char)[data-step='K']`:
184
+
185
+ ```css
186
+ /* step 2 eases in slowly with a bounce; the others snap */
187
+ blink-wc.light :is(.blink-content, .blink-char)[data-step='2'] {
188
+ --blink-step-ease: 0.5s;
189
+ --blink-step-timing: cubic-bezier(0.34, 1.56, 0.64, 1);
190
+ }
191
+ ```
192
+
193
+ ### Per-letter sequences
194
+
195
+ By default the whole text steps together. Set **`step-by="letter"`** (or
196
+ `"word"`) and every unit runs the sequence offset by its index (by
197
+ `--blink-stagger`), so the states **ripple** across the text — a wave of colour,
198
+ depth, or transform. It works with the built-in step modes and your own:
199
+
200
+ ```html
201
+ <blink-wc mode="depth" step-by="letter" rate="0.6s">RIPPLE</blink-wc>
202
+ ```
203
+
204
+ The step driver freezes when paused, off-screen, or in a hidden tab, and rests
205
+ at step 0 under `prefers-reduced-motion`.
206
+
207
+ ## API
208
+
209
+ ```javascript
210
+ const el = document.querySelector('blink-wc');
211
+
212
+ el.start(); // play-state="running"
213
+ el.stop(); // play-state="paused"
214
+ el.toggle(); // flip between the two
215
+ el.refresh(); // re-render content after a dynamic change
216
+
217
+ el.rate; // '1s' (readonly)
218
+ el.behavior; // 'blink' | 'pulse' | 'flicker' | 'steps' (readonly)
219
+ el.steps; // number of steps in the multi-step engine (readonly)
220
+ el.playState; // 'running' | 'paused' (readonly)
221
+ el.mode; // active mode preset, e.g. 'neon' | 'morse' | 'extrude' | '' (readonly)
222
+ ```
223
+
224
+ Set attributes to change configuration (`el.setAttribute('rate', '0.5s')`); the
225
+ component reacts to attribute changes automatically.
226
+
227
+ ## Events
228
+
229
+ ```javascript
230
+ el.addEventListener('blink-start', () => console.log('running'));
231
+ el.addEventListener('blink-pause', () => console.log('paused'));
232
+ el.addEventListener('blink-cycle', () => console.log('one blink completed'));
233
+ ```
234
+
235
+ All events bubble.
236
+
237
+ ## CSS Custom Properties
238
+
239
+ ```css
240
+ blink-wc {
241
+ --blink-rate: 1s; /* duration of one cycle (also set via the `rate` attr) */
242
+ --blink-min-opacity: 0; /* opacity of the "off" phase */
243
+ --blink-stagger: 0.08s; /* per-unit delay step for letter modes */
244
+ --blink-step-ease: 0s; /* transition between steps (0 = hard cut) */
245
+ --blink-dot: 170; /* Morse dot unit, in ms */
246
+ --blink-bg: …; /* themed-variant background */
247
+ --blink-fg: …; /* themed-variant foreground */
248
+ }
249
+ ```
250
+
251
+ ## Using with Vanilla Breeze
252
+
253
+ `<blink-wc>` reads Vanilla Breeze design tokens (`--color-accent`,
254
+ `--color-error`, `--color-warning`, `--color-success`, `--font-mono`,
255
+ `--font-sans`, `--radius-m`, …) through layered `var()` fallbacks. A `--blink-*`
256
+ override always wins; absent that, a VB token is used; absent that, a built-in
257
+ default applies. Apply a VB theme to the page and the themed variants inherit it
258
+ automatically:
259
+
260
+ ```html
261
+ <html data-theme="…">
262
+ <blink-wc mode="neon">Reads --color-accent</blink-wc>
263
+ </html>
264
+ ```
265
+
266
+ No configuration is required, and pages without Vanilla Breeze render via the
267
+ built-in fallbacks.
268
+
269
+ How it fits the VB system:
270
+
271
+ - **Light/dark aware.** Tokens are resolved per element, so `data-mode="light"` /
272
+ `"dark"` and VB's `light-dark()` tokens re-skin every mode. Text on coloured
273
+ panels (`warning`, `alert`) is computed black-or-white from the panel colour,
274
+ so it stays readable on any theme (including `a11y-high-contrast`) and with a
275
+ custom `--blink-bg`. `crt` is always a dark screen whose phosphor takes the
276
+ theme's `--color-success` hue.
277
+ - **Cascade layers.** All styles live in `@layer web-components`, using VB's own
278
+ layer order. VB themes and packs (`bundle-theme` and above) and any unlayered
279
+ author CSS override `<blink-wc>` without specificity fights.
280
+ - **Motion Effects toggle.** Turning off "Motion Effects" in VB's
281
+ `<theme-picker>` (which sets `:root[data-motion-reduced]`) stops `<blink-wc>`
282
+ exactly like `prefers-reduced-motion`, live, without a reload.
283
+
284
+ Relies on CSS relative colour syntax and `light-dark()` (Baseline 2024).
285
+
286
+ ## Accessibility
287
+
288
+ - `prefers-reduced-motion: reduce` — or Vanilla Breeze's
289
+ `:root[data-motion-reduced]` — halts all animation (including the terminal and
290
+ typewriter carets) and shows the text fully, unless you explicitly set
291
+ `reduced-motion="ignore"`. Changes apply live.
292
+ - Screen readers hear the text once, as written. Letter and word modes split
293
+ the text into animated spans, so those spans are hidden from assistive tech
294
+ and a visually hidden copy of the text is exposed instead (unless the content
295
+ holds links or other interactive elements, which are left as authored). The
296
+ terminal cursor, typewriter caret and morse dot/dash caption are decorative
297
+ and are not announced.
298
+ - The blink pauses when off-screen and when the tab is hidden.
299
+ - `pause-on-hover` also pauses on keyboard focus (`:focus-within`).
300
+ - Avoid fast, large-area blinking, which can trigger photosensitive seizures
301
+ (keep flashes below ~3 Hz and small).
302
+
303
+ ## Development
304
+
305
+ ```bash
306
+ npm install # Install dependencies
307
+ npm run dev # Start dev server (open /demo.html or /test/test-page.html)
308
+ npm run build # Build dist/ (ES module + CSS)
309
+ npm run test # Run Playwright tests
310
+ npm run lint # Lint src/
311
+ npm run format # Format with Prettier
312
+ npm run analyze # Regenerate custom-elements.json
313
+ ```
314
+
315
+ The documentation site lives in `docs/` (served via GitHub Pages). It loads
316
+ Vanilla Breeze and its `<theme-picker>` from a CDN to demonstrate the theme
317
+ integration live. `npm run build` refreshes the component copy in `docs/`.
318
+
319
+ ## License
320
+
321
+ MIT
package/blink-wc.d.ts ADDED
@@ -0,0 +1,113 @@
1
+ /**
2
+ * <blink-wc> — the deprecated <blink> element, reborn for 2026. A modern,
3
+ * accessible, themeable web component for blinking content. Light DOM,
4
+ * CSS-driven, and Vanilla Breeze design-token aware.
5
+ */
6
+
7
+ export type BlinkBehavior = 'blink' | 'pulse' | 'flicker' | 'steps';
8
+ export type BlinkPlayState = 'running' | 'paused';
9
+ export type BlinkReducedMotion = 'respect' | 'ignore';
10
+
11
+ /**
12
+ * Visual/motion preset. Surface themes style the container; letter modes split
13
+ * the text and animate each unit; `morse` blinks the text out in code; the
14
+ * step modes (`extrude`, `collapse`, `outline`, `morph`) ride the multi-step
15
+ * engine, giving each step of the cycle its own style.
16
+ */
17
+ export type BlinkMode =
18
+ | ''
19
+ | 'neon'
20
+ | 'crt'
21
+ | 'terminal'
22
+ | 'warning'
23
+ | 'alert'
24
+ | 'police'
25
+ | 'vegas'
26
+ | 'morse'
27
+ | 'wave'
28
+ | 'twinkle'
29
+ | 'sparkle'
30
+ | 'rainbow'
31
+ | 'chase'
32
+ | 'glitch'
33
+ | 'cascade'
34
+ | 'typewriter'
35
+ | 'zoom'
36
+ | 'flip3d'
37
+ | 'swing'
38
+ | 'shake'
39
+ | 'heartbeat'
40
+ | 'decode'
41
+ | 'extrude'
42
+ | 'collapse'
43
+ | 'outline'
44
+ | 'morph'
45
+ | 'depth'
46
+ | 'revolve';
47
+
48
+ /** Granularity for the per-unit motion modes. */
49
+ export type BlinkUnit = 'letter' | 'word';
50
+
51
+ /** Whether the step engine advances the whole element or each unit. */
52
+ export type BlinkStepBy = 'element' | 'letter' | 'word';
53
+
54
+ export interface BlinkWcEventMap {
55
+ 'blink-start': CustomEvent<void>;
56
+ 'blink-pause': CustomEvent<void>;
57
+ 'blink-cycle': CustomEvent<void>;
58
+ }
59
+
60
+ export declare class BlinkWc extends HTMLElement {
61
+ static get observedAttributes(): string[];
62
+
63
+ /** Duration of one blink cycle, as a CSS time. Attribute: `rate`. Default `1s`. */
64
+ get rate(): string;
65
+ /** Blink style. Attribute: `behavior`. Default `blink`. */
66
+ get behavior(): BlinkBehavior;
67
+ /** Opacity of the "off" phase (0–1), or `null` when unset. Attribute: `min-opacity`. */
68
+ get minOpacity(): string | null;
69
+ /** Iteration count, or `infinite`. Attribute: `count`. Default `infinite`. */
70
+ get count(): string;
71
+ /** Number of steps in the multi-step engine (min 2). Attribute: `steps`. */
72
+ get steps(): number;
73
+ /** Per-step hold weights, or `null`. Attribute: `step-durations`. */
74
+ get stepDurations(): string | null;
75
+ /** Whether the step engine steps the whole element or each unit. Attribute: `step-by`. */
76
+ get stepBy(): BlinkStepBy;
77
+ /** Playback state. Attribute: `play-state`. Default `running`. */
78
+ get playState(): BlinkPlayState;
79
+ /** Active visual/motion preset. Attribute: `mode`. Default `''` (none). */
80
+ get mode(): BlinkMode;
81
+ /** Granularity for the motion modes. Attribute: `unit`. Default `letter`. */
82
+ get unit(): BlinkUnit;
83
+
84
+ /** Start blinking (sets `play-state="running"`). */
85
+ start(): void;
86
+ /** Stop blinking (sets `play-state="paused"`). */
87
+ stop(): void;
88
+ /** Toggle between running and paused. */
89
+ toggle(): void;
90
+ /** Re-render content and recompute (call after dynamic content changes). */
91
+ refresh(): void;
92
+
93
+ connectedCallback(): void;
94
+ disconnectedCallback(): void;
95
+ attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
96
+
97
+ addEventListener<K extends keyof BlinkWcEventMap>(
98
+ type: K,
99
+ listener: (this: BlinkWc, ev: BlinkWcEventMap[K]) => unknown,
100
+ options?: boolean | AddEventListenerOptions
101
+ ): void;
102
+ addEventListener(
103
+ type: string,
104
+ listener: EventListenerOrEventListenerObject,
105
+ options?: boolean | AddEventListenerOptions
106
+ ): void;
107
+ }
108
+
109
+ declare global {
110
+ interface HTMLElementTagNameMap {
111
+ 'blink-wc': BlinkWc;
112
+ }
113
+ }