npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@uqrealitylabs/eyslie

v0.6.0

Published

Expressive living text eyes, moods, reactive mouths, blush, bubbles, and localized reactions for React.

Readme

eyslie

Expressive living text for React: eyes on any grapheme, gaze, winks, moods, blush, emotion-linked mouths, thought bubbles, and original visual atmospheres.

@uqrealitylabs/eyslie is DOM/CSS/React only. It has no canvas, animation framework, route logic, or bundled brand art.

Install

npm install @uqrealitylabs/eyslie

The package is ESM-only. Import its CSS explicitly:

import { LivingText } from "@uqrealitylabs/eyslie";
import "@uqrealitylabs/eyslie/styles.css";

Put Eyes Anywhere

Turn on eyeMarkers, then place a marker immediately before any Unicode grapheme:

  • < gives the next character a resting left gaze.
  • ^ gives the next character a resting upward gaze.
  • > gives the next character a resting right gaze.

Markers are removed from both the rendered word and its default accessible label. Escape a literal marker or backslash with \ (\^, \<, \>, \\). A trailing marker stays literal. When several markers precede one grapheme, the last direction wins.

An explicit direction marker wins over the global gaze behaviour. Eye emoji are the deliberate exception: they always stay centred.

<LivingText
  text="W^O>W!"
  eyeMarkers
  mood="excited"
  eyeShape="star"
  eyeStyle="cosmic"
  gaze="softFollow"
  blink="wink"
  expression="theatrical"
  bubbleStyle="comic"
  theme="tinyGalaxy"
  mouth="auto"
/>

This displays and announces WOW!; the O looks up and the second W rests to the right. Marker parsing is opt-in, so ordinary <, ^, and > text remains unchanged by default.

The standalone 👁 and emoji-style 👁️ graphemes are always treated as a single classic eye. Their platform glyph is visually replaced to avoid a double eye, and their pupil stays centred even when tracking, scanning, or a direction marker is active. The emoji remains in the accessible label.

The original two-anchor API remains available:

<LivingText
  text="JOIN US"
  eyeLetters={{ primary: "O", secondary: "U" }}
/>

Selectors may be a grapheme value or zero-based grapheme index.

Expression Controls

The global controls stay orthogonal so an atmosphere never dictates an emotion or behaviour:

| Prop | Values | Default | | --- | --- | --- | | mood | idleCurious, nearStartled, excited, blush, celebration, sadShrivel, recovery | idleCurious | | eyeShape | round, almond, square, star, heart, visor, diamond, droplet | round | | eyeStyle | classic, ink, pixel, neon, cosmic, paper, outline, gloss | classic | | gaze | follow, softFollow, centered, sideGlance, wander, scan | follow | | blink | natural, wink, none | natural | | expression | subtle, playful, theatrical | playful | | bubbleStyle | cloud, comic, whisper, pixel | cloud | | theme | any exported livingTextThemes key | classic | | mouth | none, auto, smile, grin, open, flat, frown, pout | none | | smile | deprecated boolean alias for mouth="auto" | false | | blush | "auto", true, false | "auto" | | thoughtLang | BCP-47 language tag for thought text | unset |

natural blinks the second eye, matching the original O/U behaviour. wink cycles through all resolved eyes and works with a single eye. Inline markers support any number of eyes.

Blush cheeks are anchored to the visible characters near each text edge rather than percentages of the component box. blush="auto" follows the blush mood; true and false override it independently. mouth="auto" changes lip shape with mood and only uses the shy pout when blush is actually visible. Eyslie is intended for single-line words and short labels, not paragraphs or multiline layout.

The demo's emotion spectrum combines an explicit mood with the three-stop expression intensity. getExpressionLevel(number) clamps a 0–2 value and resolves the nearest subtle, playful, or theatrical level, then the complete locale phrase for that mood is selected. It never assembles translated fragments or infers an emotion from a country.

Thoughts

Pass a partial mood map to replace or suppress the neutral defaults:

<LivingText
  text="^H>I"
  eyeMarkers
  mood="celebration"
  thoughts={{ celebration: "hooray!", sadShrivel: "" }}
/>

Localized Thoughts with Seer

The demo uses @keys-i/seer at build time to validate complete thought maps. Seer never enters Eyslie's browser bundle or published runtime; the selected map still reaches the component through the existing thoughts prop.

Language and expression are deliberately separate. A canonical BCP-47 locale selects wording, while the explicit expression prop selects the shared subtle, playful, or theatrical tone. A country never chooses a personality or emotion automatically.

The demo resolves an exact locale, then a compatible language and script, then base language when no script was requested. The und fallback suppresses the thought bubble instead of guessing a language or culturally neutral reaction. Starter packs are included for English, Spanish, Arabic, Japanese, and Simplified Chinese; do not describe other locales as translated, and have native speakers review copy used in a specific community.

Load Seer once at build or server start, select the complete map, and serialize the same locale and thoughts into the first render so hydration is deterministic:

<LivingText
  text="^H>I"
  eyeMarkers
  mood="celebration"
  expression={expression}
  thoughts={content.eyslie.thoughts[expression]}
  thoughtLang={locale}
/>

Thought bubbles use dir="auto" and bounded wrapping for RTL and longer text. They are decorative and are not announced separately from LivingText's stable accessible name. If a reaction conveys meaning, announce it in the surrounding application with a correctly tagged live region, as the demo does.

Atmospheres and Original Worlds

livingTextThemes contains original palettes such as harbourDawn, desertTwilight, rainforestMist, cityAfterDark, polarGlow, storybookInk, retroArcade, tinyGalaxy, and solarpunkGarden.

They change colour atmosphere only. They do not assign behaviours or emotions to countries, cultures, or communities. No built-in theme copies a franchise, character, logo, or signature protected design. Use the colour props or CSS custom properties for a community-reviewed local palette or artwork you have permission to use.

Explicit colour props override a selected theme, and values in style override both:

<LivingText
  text="^H>I"
  eyeMarkers
  theme="harbourDawn"
  excitedColor="#ff4f81"
  style={{ "--eyslie-blush-color": "#ef8baa" } as React.CSSProperties}
/>

Accessibility and Motion

The animated letters are hidden from assistive technology. LivingText puts a stable accessible name on its wrapper, automatically stripping inline control markers. Pass ariaLabel when the spoken phrase should differ from the visible text.

Do not use stylised living text as the only label for a critical action unless the surrounding button or link also has a stable accessible name.

Set reducedMotion to disable JavaScript tracking and wink timers. CSS also honours the operating system's reduced-motion preference while preserving fixed marker gaze. Server output and the first client render are deterministic.

React Server Component frameworks keep LivingText and hooks behind their client boundaries. Pure grapheme, marker, state, timing, and geometry helpers remain callable from the package root on the server.

Deterministic Helpers

The root export includes:

  • parseEyeMarkers(text)
  • splitTextLetters(text)
  • getCheekAnchors(letters)
  • createWinkSchedule(seed)
  • constrainPupilOffset(x, y, bounds)
  • nextLivingTextMood(mood, event, elapsedMs)
  • isPointerNear(rect, point, radius)

Interactive Demo

Run npm run demo:dev. A production build is written to demo-dist/ by npm run demo:build, and npm run demo:preview serves it locally.

GitHub Pages publishes the demo at https://uqrealitylabs.com/eyslie/ with the matching /eyslie/ asset base. The display font is the existing OFL-licensed Pixelify Sans asset; its notice remains beside the source and built asset.

Development

The published library supports Node.js 22.18 or newer. Repository development uses Node.js 24.18 or newer because Seer is a build-time tool.

npm install
npm run typecheck
npm run lint
npm run format:check
npm run test
npm run build
npm run demo:build
npm run benchmark

License

See LICENSE.