@be-enlighten/beni-avatar

Avatar Vue 3 animado (mascote Beni) — squircle que olha pra você, pisca, pula e comemora. Estados, humores e acessórios, animações a 60fps com GSAP.
893
Install
npm install @be-enlighten/beni-avatar
Latest Version:0.4.2
Vue:3
License:MIT
Last Updated:Sep 11, 2026
Links: npm
Maintainer: hardeath950

@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 gsap e vue

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. O gsap (≥ 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.css antes 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

bloub-to-vue-and-react

SVG recreation of the x.ai bot avatar. One shape morphing through 14 states, mea...

330 1,854
wisspop

Modales y dropdowns con animaciones ancladas al origen: el panel nace del elemen...

1,970 0
v-gsap-nuxt

GSAP Directive for Nuxt 3 with SplitText support

80,470 170
@mrbubbaz/v-gsap-nuxt

GSAP Directive for Nuxt 3 with SplitText support

1,477 0

Version History

Version Released Vue License
0.4.2 ^3.5 MIT
0.4.1 ^3.5 MIT
0.4.0 ^3.5 MIT