@be-enlighten/beni-avatar
| Install | |
|---|---|
npm install @be-enlighten/beni-avatar |
|
| Latest Version: | 0.4.2 |
| Vue: | 3 |
| License: | MIT |
| Last Updated: | Sep 11, 2026 |
| Links: | npm |
@be-enlighten/beni-avatar
Avatar Vue 3 animado, pequeno e simpático. Um squircle que olha pra você, pisca, pula e comemora. Solte-o em qualquer app Vue pra dar personalidade a estados de carregamento, páginas vazias, fluxos de onboarding, ou só por diversão.
- 🎭 8 estados:
idle,working,error,speaking,loading,sleep,branding,celebrate - 💬 4 humores:
happy,sad,angry,surprised(sobrepostos a qualquer estado) - 🎩 6 acessórios de cabeça: beanie, coroa, capacete, chapéu de festa, gorro de papai noel, gorro stan
- 🖱️ Olhos que seguem o mouse, opcional
- 🎉 Easter eggs por clique: duplo, triplo, quíntuplo
- 🎨 Tema via props: cores do corpo e dos olhos controladas pelo componente pai
- ⚡ ~14 KB gzipped, sem dependências de runtime além de
gsapevue
Animações a 60fps com GSAP.
Demo
A playground em examples/beni-avatar é um estúdio interativo: à esquerda, os 8 estados como cards clicáveis; no centro, o Beni grande controlado pela seleção; à direita, os 4 humores. Logo abaixo, um painel de configuração com acessório, tema (5 presets), toggle "seguir mouse" e slider de intervalo de piscada. Os cards reagem ao tema e ao acessório ativos, com borda gradiente destacada no item selecionado. É também a página hospedada na Vercel.
Instalação
pnpm add @be-enlighten/beni-avatar gsap
# ou
npm install @be-enlighten/beni-avatar gsap
# ou
yarn add @be-enlighten/beni-avatar gsap
Peer dependency:
vue≥ 3.5. Ogsap(≥ 3.15) é dependency do pacote — instalado automaticamente; declare-o também no app se quiser pinar a mesma cópia.
Início rápido
O avatar é distribuído como um SFC Vue acompanhado de um arquivo CSS. Importe o stylesheet uma vez no app, em main.ts, para que as variáveis CSS dos acessórios fiquem disponíveis:
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import '@be-enlighten/beni-avatar/style.css'
createApp(App).mount('#app')
<!-- App.vue -->
<script setup lang="ts">
import { ref } from 'vue'
import { Beni, type BeniState, type MoodName } from '@be-enlighten/beni-avatar'
const state = ref<BeniState>('idle')
const mood = ref<MoodName | null>(null)
</script>
<template>
<Beni
v-model:state="state"
v-model:mood="mood"
/>
</template>
Pronto. Você tem um squircle de ~160×160 que pisca, respira e segue o cursor.
Props
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
state* |
BeniState |
'idle' |
Estado atual. Bind via v-model:state. |
mood* |
MoodName | null |
null |
Humor sobreposto. Bind via v-model:mood. |
headItem |
HeadItemKey | null |
null |
Acessório: 'beanie', 'crown', 'helmet', 'partyhat', 'santahat', 'stanhat'. |
bodyBaseColor |
string |
'#60a5fa' |
Cor inicial do gradiente do corpo. |
bodyBaseColorTo |
string |
'#2563eb' |
Cor final do gradiente do corpo. |
bodyBaseModel |
BodyModelName |
'squircle' |
Forma base, antes de overrides de estado. |
bodyScale |
number |
1 |
Escala geral do corpo. |
bodyOffsetX |
number |
0 |
Deslocamento horizontal, em unidades SVG. |
bodyOffsetY |
number |
0 |
Deslocamento vertical, em unidades SVG. |
eyeBaseColor |
string |
'#00f0ff' |
Cor padrão dos olhos (estados podem sobrescrever). |
eyeBaseBlinkInterval |
number |
4000 |
Intervalo médio entre piscadas, em ms. 0 desativa. |
eyeGap |
number |
2 |
Distância entre os olhos. |
eyeOffsetX |
number |
0 |
Deslocamento horizontal dos olhos. |
eyeOffsetY |
number |
2 |
Deslocamento vertical dos olhos. |
eyeLookAtMouse |
boolean |
true |
Olhos seguem o cursor. |
eyeLookAtTarget |
{ x, y } | null |
null |
Fixa os olhos em um ponto (sobrepõe o mouse). |
eyeLookDuration |
number |
0.15 |
Duração do tween de movimento, em segundos. |
eyeLookEase |
string |
'power2.out' |
Ease do GSAP. |
eyeLookOvershoot |
number |
0.6 |
Overshoot quando os olhos se acomodam no alvo. |
bodyFollowMouse |
boolean |
true |
Quando os olhos atingem as bordas, a cabeça vira levemente na direção do cursor. |
bodyFollowMaxRotation |
number |
4 |
Rotação máxima da cabeça, em graus. |
bodyFollowDeadzone |
number |
5 |
Limiar (em unidades do offset dos olhos) abaixo do qual a cabeça relaxa. |
bodyFollowDuration |
number |
0.45 |
Duração do tween de rotação da cabeça, em segundos. Mais lento que os olhos cria o efeito de "pescoço acompanha". |
clickRipple |
boolean |
true |
Mostra uma onda no clique. |
clickRippleColor |
string | null |
null |
Cor da onda. Usa a cor atual do corpo por padrão. |
clickRippleDuration |
number |
0.4 |
Duração da onda, em segundos. |
* bind bidirecional via v-model.
Eventos
| Evento | Payload | Quando |
|---|---|---|
update:state |
BeniState |
O estado muda (via v-model). |
update:mood |
MoodName | null |
O humor muda. |
click |
BeniClickPayload |
O avatar é clicado. |
interaction |
BeniInteractionPayload |
Uma interação interna de clique dispara. |
Estados
| Estado | Visual | Melhor uso |
|---|---|---|
idle |
Balanço suave, olhos passeando | Padrão. |
working |
Corpo girando, olhos frenéticos | Carregamento/processamento. |
error |
Corpo vermelho, tremor com glitch | Estados de erro. |
speaking |
Cabeça balançando, sílabas na boca | Assistentes de IA. |
loading |
Quadrado arredondado girando | Loaders. |
sleep |
Respiração lenta, olhos fechados, "Z"s | Ocioso / segundo plano. |
branding |
Magenta→índigo, olhos "EN" | Splash / marketing. |
celebrate |
Pulando, explosão de confete | Sucesso. |
<Beni :state="currentState" />
const currentState = ref<BeniState>('working')
watch(someTask, (task) => {
currentState.value = task.status === 'loading' ? 'working' : 'celebrate'
})
Humores
Os humores se sobrepõem a um estado. Mudam olhos, boca e geram pequenos elementos SVG (lágrima, veia de raiva).
| Humor | Efeito |
|---|---|
happy |
Olhos arqueados, sorriso largo. |
sad |
Olhos fechados, tom azulado, lágrima. |
angry |
Olhos arqueados pra baixo, tom vermelho, veia. |
surprised |
Olhos circulares, sem boca. |
<Beni v-model:state="state" v-model:mood="mood" />
Você pode derivar o humor a partir do estado. O avatar já dispara happy no clique duplo em idle (veja "Interações internas").
Acessórios de cabeça
Seis itens prontos. Cada um acompanha o pulo/escala/rotação do corpo, mas se mantém firme em cima da cabeça — não inclina com a inclinação do corpo, na prática parecia instável demais.
<Beni :head-item="accessory" />
import { type HeadItemKey, headItemNames, headItemLabels } from '@be-enlighten/beni-avatar'
import { ref } from 'vue'
const accessory = ref<HeadItemKey | null>('crown')
Em working, loading e error o acessório é escondido automaticamente. Esses estados giram tanto o corpo que o chapéu voaria.
Tematizando os acessórios: as cores vêm de variáveis CSS. Sobrescreva-as no seu stylesheet:
:root { --beni-items-head-crown: #ffd700; --beni-items-head-crown-shade: #b8860b; --beni-items-head-beanie: #8b5cf6; /* ... */ }Importe
@be-enlighten/beni-avatar/style.cssantes e sobrescreva as variáveis depois.
Interações internas por clique
Clique rápido pra disparar easter eggs. Já vêm de fábrica, sem flag.
| Padrão | Estado inicial | Dispara |
|---|---|---|
| Clique duplo | idle |
Humor happy por 2,2s |
| 5 cliques | idle |
Estado branding por 3s |
| Clique duplo | sleep |
Piscada de volta por 1,5s |
| 3 cliques | sleep |
Acorda (persistente) |
Cada interação é anunciada pelo evento interaction.
Eventos de clique
<Beni @click="onClick" @interaction="onInteraction" />
import type { BeniClickPayload, BeniInteractionPayload } from '@be-enlighten/beni-avatar'
function onClick(p: BeniClickPayload) {
console.log(p.target) // 'body' | 'eye-left' | 'eye-right'
console.log(p.clickIndex) // 1 no primeiro clique, 2 no segundo dentro de 800ms, ...
console.log(p.state) // estado atual
console.log(p.mood) // humor atual (ou null)
}
function onInteraction(p: BeniInteractionPayload) {
console.log(p.id) // ex.: 'happy-on-double-click'
console.log(p.label) // legível por humanos
console.log(p.triggerClickCount)
}
Tematizando o avatar
<script setup lang="ts">
const theme = {
body: '#f59e0b',
bodyTo: '#ef4444',
eye: '#fef3c7',
}
</script>
<template>
<Beni
:body-base-color="theme.body"
:body-base-color-to="theme.bodyTo"
:eye-base-color="theme.eye"
/>
</template>
Para um seletor de temas completo, use computed:
import { computed } from 'vue'
import type { BeniState, MoodName } from '@be-enlighten/beni-avatar'
const theme = computed(() => themes[currentTheme.value])
const state = ref<BeniState>('idle')
const mood = ref<MoodName | null>(null)
O avatar é totalmente reativo: cada mudança de prop entra com tween (sem piscar), e estados/humores fazem crossfade suave.
Exemplo: estado de carregamento de um assistente de IA
<script setup lang="ts">
import { ref, watch } from 'vue'
import { Beni, type BeniState, type MoodName } from '@be-enlighten/beni-avatar'
const status = ref<'idle' | 'thinking' | 'done'>('idle')
const state = ref<BeniState>('idle')
const mood = ref<MoodName | null>(null)
watch(status, (s) => {
if (s === 'thinking') {
state.value = 'working'
mood.value = null
} else if (s === 'done') {
state.value = 'celebrate'
mood.value = 'happy'
} else {
state.value = 'idle'
mood.value = null
}
})
</script>
<template>
<div class="assistant">
<Beni
v-model:state="state"
v-model:mood="mood"
:head-item="status === 'done' ? 'partyhat' : null"
body-base-color="#fbbf24"
body-base-color-to="#ec4899"
eye-base-color="#fef3c7"
/>
<p v-if="status === 'thinking'">Pensando…</p>
<p v-else-if="status === 'done'">Pronto!</p>
</div>
</template>
TypeScript
Pacote totalmente tipado. Exports mais úteis:
import type {
BeniState,
MoodName,
HeadItemKey,
BodyModelName,
EyeModelName,
BeniClickPayload,
BeniInteractionPayload,
} from '@be-enlighten/beni-avatar'
// Constantes
import {
beniStateNames, // ['idle', 'working', ...]
beniMoods,
headItemNames,
headItemLabels,
} from '@be-enlighten/beni-avatar'
Suporte a navegadores
Qualquer navegador com suporte a CSS moderno (custom properties, filter) e SVG, ou seja, todos os navegadores evergreen. O GSAP exige requestAnimationFrame, que é universal.
O pacote respeita prefers-reduced-motion: reduce: as animações pausam no frame final quando o usuário tem essa preferência ativa.
Desenvolvimento
Este pacote vive no monorepo en-sdk (packages/beni-avatar). A demo/playground fica em examples/beni-avatar e consome o dist/ deste pacote via workspace — rode o build antes (e após cada mudança no src/).
pnpm install
pnpm --filter @be-enlighten/beni-avatar build # gera dist/ (pacote)
pnpm --filter @be-enlighten/beni-avatar typecheck # só type-check
pnpm --filter @be-enlighten/enspace-sdk-example-beni dev # playground em http://localhost:5173
Publicando
Versão própria — este pacote não entra no lockstep dos 4 packages enspace-sdk-*. O versionamento é gerenciado por changesets (.changeset/ no root do monorepo): adicione um changeset com pnpm changeset a cada mudança user-facing e publique pelo fluxo unificado do monorepo:
pnpm version-packages # bumpa versão + gera CHANGELOG a partir dos changesets
pnpm release # valida, builda e publica o que estiver pendente
Detalhes na seção Publicação do AGENTS.md do monorepo. Sobe no npm público (access: public). Precisa de login npm local (npm login) com acesso ao escopo @be-enlighten.
Licença
MIT.
Related Packages
SVG recreation of the x.ai bot avatar. One shape morphing through 14 states, mea...
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 0.4.2 | ^3.5 | MIT | |
| 0.4.1 | ^3.5 | MIT | |
| 0.4.0 | ^3.5 | MIT |