EchoSistema Template
Índice da documentação

Documentação do template

O guia do starter EchoSistema: como rodar, onde cada coisa mora e como usar os tokens e componentes do sistema.

Introdução

Starter Nuxt dos sites de plataforma do grupo: o Liquid Glass inteiro parametrizado por quatro tokens de marca.

Tokens de marca

Quatro hex e um gradiente derivam todo o CSS por color-mix; trocar a paleta é editar um bloco.

Tema claro e escuro

Paridade automática: todo valor tematizável tem gêmeo dark, e o tema certo pinta antes do primeiro paint.

Três idiomas

Espanhol canônico na raiz, pt-BR e inglês prefixados, catálogo único e locales em lockstep.

Componentes nativos

Dialog, details e selects do navegador vestidos pelo sistema: acessibilidade de graça.

Início rápido

pnpm instala, roda e valida. Os mesmos scripts do package.json, sem passos escondidos.

ComandoPapel
pnpm devServidor de desenvolvimento com hot reload.
pnpm buildBuild de produção (SSR).
pnpm generateSite estático pré-renderizado.
pnpm previewServe o build de produção localmente.
pnpm lintESLint no projeto inteiro.
pnpm typecheckvue-tsc sobre o app e a configuração.

Estrutura

Layout padrão do Nuxt 4: app/ é o site, i18n/ os textos, shared/ o que servidor e cliente compartilham.

app/
  assets/css/main.css
  components/
  composables/
  data/
  layouts/
  pages/
  plugins/
  utils/
i18n/locales/
shared/
nuxt.config.ts

main.css é o único ponto de entrada de estilo: arquivos .vue não carregam style. O catálogo de idiomas mora em shared/languages.ts e a navegação em app/data/nav.ts.

Tokens de marca

O bloco MARCA no topo do main.css é o único lugar com hex de marca.

Fundos, textos, vidro, botões e o painel de código derivam destes quatro tokens via color-mix, com paridade claro/escuro automática. Para rebatizar um site derivado, troque este bloco e nada mais; as paletas agro e consumer ficam comentadas no arquivo como exemplo.

:root {
  --brand-1: #426ab2;
  --brand-2: #40c1c3;
  --brand-deep: #040b2d;
  --brand-link: #3d91cf;
}

Tema e idiomas

O tema vira em html[data-theme]; o idioma é dono da URL.

data-theme

Um script pré-paint no head lê a preferência guardada (ou a do sistema) e define data-theme antes do primeiro paint: sem flash de tema errado. A troca em runtime é o mesmo atributo.

prefix_except_default

O espanhol canônico vive sem prefixo; /pt-BR e /en prefixam as demais rotas. Adicionar um idioma é uma entrada no catálogo de shared/languages.ts mais o arquivo de locale correspondente.

Largura do conteúdo

Uma régua para o masthead, as páginas e o rodapé - e três valores para ela.

Tudo que usa `.es-shell` segue a mesma régua, e é por isso que a preferência do leitor move a barra e a página juntas. Antes de existir a classe, cada arquivo escrevia a sua e a home nascia mais estreita que a barra por cima dela. Docs e painel são SEMPRE largos e dizem isso com `.es-shell--full`.

ValorRégua
compact90rem · a coluna de leitura
extended110rem · o padrão de fábrica
full- · sem teto, às extremidades
# .env - a específica vence a genérica
NUXT_TEMPLATE_THEME_WIDTH=extended
NUXT_THEME_WIDTH=extended

O valor é assado no build: ele viaja no script que pinta a largura antes do primeiro quadro, e largura chegando depois da hidratação é um salto de layout na cara de quem lê. Depois de mudar o .env, refaça o build. A engrenagem flutuante troca por leitor, e essa escolha fica no navegador dele.

Tipografia

Conteúdo semântico se veste pelo contêiner: .es-prose estiliza os elementos de dentro.

Títulos, parágrafos, citações, código e os demais elementos de texto ganham o estilo do sistema ao entrar numa .es-prose, sem utility por elemento. As famílias carregadas são Manrope, Montserrat e JetBrains Mono.

<div class="es-prose">
  <h2>…</h2>
  <p>…</p>
  <blockquote>…</blockquote>
</div>
Ver a referência de tipografia

Inventário

Os 41 componentes de app/components/echo, por papel. Cada um veste um elemento nativo quando existe um: é daí que vêm teclado, foco e anúncio, sem reimplementação.

Ações e navegação

Button · Chip · Badge · Pill · Dropdown · Pagination · Breadcrumb · Tabs

Formulários

Field · Input · Textarea · Select · SearchSelect · TagsInput · Checkbox · Radio · Switch · Slider · Rating · FileButton · Dropzone · DatePicker · PhoneInput

Retorno e espera

Alert · Modal · Drawer · Toaster · Block · Skeleton · Spinner · Progress · Ring

Conteúdo

Card · List · ListItem · DataTable · Avatar · Gallery · Tree · Timeline · Collapse · Divider · Stat · Player

Mídia e códigos

Chart · Map · Carousel · Editor · ImageCropper · ImageViewer · QrCode · Barcode

Calendário

Calendar · CalendarMini · app/utils/calendar.ts

Modais de conta

LoginForm · RegisterForm · ForgotPasswordForm · UpdatePasswordForm · PageShell · LoginModal · RegisterModal · ForgotPasswordModal · UpdatePasswordModal · LockModal · LoginPrompt

Chrome (barras, rodapés, trilhos)

MastheadEcommerce · MastheadRealEstate · MastheadLearn · MastheadHelpDesk · MastheadLanding · FooterEcommerce · FooterRealEstate · FooterLearn · FooterHelpDesk · FooterLanding · SidebarPanel · SidebarDocs · SidebarFilters · ElementThemeSwitch · ElementLanguageSwitch · ElementCurrencySwitch · ElementSwitchDropdown

Intenções e variantes

Dois eixos que nunca se combinam à mão: .es-i--* dá a cor, a variante consome.

Dez intenções definem tokens locais (--i, --i-on, --i-grad e derivados) que as quatro famílias de rótulo e o modal consomem. As variantes (filled, gradient, outline, glass, ghost) são escritas uma vez cada.

primarysuccessdangerwarningcautiondark
<echo-button variant="filled" intent="danger">…</echo-button>

<echo-chip variant="soft" intent="info">…</echo-chip>

<echo-badge variant="filled" intent="success">…</echo-badge>

<echo-pill variant="outline" intent="caution">…</echo-pill>

Alerts

Oito tons sobre a mesma paleta, com a cunha de acento na borda.

Quatro semânticos de status (info, success, warn, danger) e quatro tonais nomeados (caution, steel-blue, navy-blue, dark). O texto fica nos cinzas do tema; o acento entra no ícone, no título e na cunha.

<echo-alert tone="success" title="…">
  …
</echo-alert>

Modais

O dialog nativo: foco preso, Esc, fundo inerte e embaçado.

Superfície opaca do tema, cunha de intenção como nos alerts, fundo ilegível pelo blur enquanto aberto e rolagem travada. Clique fora não fecha; fecham o Esc, o fechar do cabeçalho e as ações do rodapé. A largura segue a escala sm, md, lg, xl e xxl.

<echo-modal ref="modal" title-id="…"
            intent="warning" size="lg">
  <template #title>…</template>
  …
  <template #footer>…</template>
</echo-modal>

modal.value?.open()

Como usar cada um

A linha mais curta que põe cada componente na tela, pronta para copiar.

Cada componente carrega o mesmo trecho no próprio arquivo, num bloco "Copy and paste" logo acima do código - é lá que está a lista completa de props. Aqui ficam só os usos mínimos, para achar o que serve sem abrir sete arquivos.

Ações e navegação

<echo-button variant="filled" intent="primary">Save</echo-button>
<echo-chip>tag</echo-chip> · <echo-badge intent="success">ok</echo-badge> · <echo-pill>4</echo-pill>
<echo-dropdown label="Account"><template #trigger>…</template>…</echo-dropdown>
<echo-tabs v-model="tab" :tabs="TABS" />
<echo-pagination v-model="page" :pages="12" />
<echo-breadcrumb :items="TRAIL" />

Formulários

<echo-field v-slot="{ id, fieldName }" label="E-mail" name="email"><echo-input :id="id" v-model="email" :name="fieldName" /></echo-field>
<echo-input v-model="text" type="email" size="sm" />
<echo-textarea v-model="note" rows="3" />
<echo-select v-model="topic"><option value="a">A</option></echo-select>
<echo-search-select v-model="country" list-id="countries" :options="names" />
<echo-date-picker v-model="birthday" mode="date" :start-year="1990" />
<echo-phone-input v-model="phone" country="br" @validity="ok = $event" />
<echo-checkbox v-model="agreed">I agree</echo-checkbox>
<echo-radio v-model="skin" name="skin" value="bordered" />
<echo-switch v-model="on" />
<echo-slider v-model="zoom" :min="1" :max="4" :step="0.01" />
<echo-rating v-model="stars" />
<echo-tags-input v-model="tags" />
<echo-file-button accept="image/*" @files="onFiles">Choose</echo-file-button>
<echo-dropzone accept="image/*" @files="onFiles">…</echo-dropzone>

Retorno e espera

<echo-alert tone="danger" title="That failed">…</echo-alert>
<echo-modal ref="el" title-id="x-title" size="md"><template #title>…</template>…</echo-modal>
<echo-drawer ref="el" title-id="y-title">…</echo-drawer>
useToast().push({ tone: 'success', title: 'Done', message: 'Saved.' })
<echo-block :loading="pending" label="Loading…">…</echo-block>
<echo-loader :loading="loading">…</echo-loader>
<echo-skeleton :lines="3" /> · <echo-spinner size="sm" />
<echo-progress :value="64" label="Progress" /> · <echo-ring :value="76" />

Conteúdo

<echo-card title="Team" subtitle="Who works here" :loading="pending">…</echo-card>
<echo-list><echo-list-item title="Row" /></echo-list>
<echo-data-table :rows="ROWS" :columns="COLS" />
<echo-avatar :src="url" name="Ada Lovelace" size="lg" />
<echo-gallery :items="IMAGES" />
<echo-tree :nodes="NODES" /> · <echo-timeline :items="STEPS" />
<echo-collapse><template #summary>…</template>…</echo-collapse>
<echo-stat label="Visits" value="24.8k" :delta="12.4" />
<echo-player :src="video" />

Mídia e códigos

<echo-chart :option="option" summary="What the chart says" height="280px" />
<echo-map :lat="-25.5" :lng="-54.6" :zoom="12" :markers="PINS" />
<echo-carousel label="Slides" loop autoplay>…</echo-carousel>
<echo-editor v-model="html" label="Body" />
<echo-image-cropper ref="el" :usages="['avatar']" mask="square" @apply="upload" />
<echo-image-viewer ref="el" :src="url" :caption="url" />
<echo-qr-code value="https://example.com" :size="180" />
<echo-barcode value="ECHO-2026-0001" format="CODE128" />

Calendário

<echo-calendar v-model:view="view" v-model:date="day" :events="EVENTS" @create="onCreate" @open="onOpen" />
<echo-calendar-mini v-model="day" :events="EVENTS" />

Modais de conta

<auth-login-form :form-id="id" :pending="sending" :error="failure" @submit="signIn" />
<auth-register-form :form-id="id" @submit="register" @login="toLogin" />
<auth-forgot-password-form :form-id="id" :sent="sent" @submit="recover" />
<auth-update-password-form :form-id="id" require-current @submit="changePassword" />
<auth-page-shell :title="title" :image="COVER" cover-title="…">…</auth-page-shell>
<auth-login-modal ref="el" :pending="sending" :error="failure" @submit="signIn" />
<auth-register-modal ref="el" @submit="register" @login="switchToLogin" />
<auth-forgot-password-modal ref="el" :sent="sent" @submit="recover" />
<auth-update-password-modal ref="el" require-current @submit="changePassword" />
<auth-lock-modal ref="el" :name="user.name" :avatar="user.avatar" @submit="unlock" />

Chrome (barras, rodapés, trilhos)

<masthead-ecommerce :links="LINKS" :departments="DEPARTMENTS" :cart-count="3" @search="onSearch" />
<masthead-learn :links="LINKS" :course="course" :done="17" :total="24" />
<masthead-help-desk :links="LINKS" :status="status" status-tone="success" :ticket-label="label" />
<masthead-landing :links="SECTIONS" :cta-label="cta" floating />
<footer-ecommerce :columns="COLUMNS" :legal="LEGAL" phone="…" email="…" />
<footer-learn :columns="COLUMNS" :cta-title="…" :cta-label="…" /> · <footer-landing :links="LINKS" :tagline="…" />
<sidebar-panel v-model:collapsed="collapsed" :groups="GROUPS" />
<sidebar-docs :groups="NAV" content-selector="#guide" /> · <sidebar-filters v-model="picked" :groups="FILTERS" />
<element-theme-switch /> · <element-language-switch compact /> · <element-currency-switch />

Modais de conta

Entrar, criar conta, recuperar e trocar a senha, e a sessão bloqueada. Cinco componentes prontos em app/components/auth/, sobre o mesmo echo-modal.

Os cinco seguem o mesmo contrato: open() e close() expostos por ref, um submit com os dados, e nada de rede por dentro. Quem sabe a URL, o token e o que fazer com a resposta é a aplicação - o componente só recolhe, valida o que não depende do servidor e devolve.

<auth-login-modal
  ref="login"
  :pending="sending"
  :error="failure"
  logo="/img/minha-marca.svg"
  title="Entrar no painel"
  @submit="signIn"
  @forgot="forgot?.open()"
  @register="register?.open()"
/>

login.value?.open()

O submit sai no vocabulário da API, não no das variáveis internas: mandar o payload direto ao endpoint é o caso comum, e traduzir nome de campo em cada site derivado seria o mesmo de-para escrito muitas vezes.

Contrato dos modais de conta
ComponenteCampos do submitEventos
<auth-*-form>os mesmos campos do modal correspondente@submit + os do fluxo
<auth-login-modal>email, password, remember@submit @forgot @register
<auth-register-modal>name, email, gender (m|f|o), birth_date (yyyy-mm-dd), password, password_confirmation, terms (1|0)@submit @login
<auth-forgot-password-modal>email@submit @login
<auth-update-password-modal>password, password_confirmation@submit
<auth-lock-modal>password@submit @sign-out

As props são as mesmas nos cinco, com duas exceções anotadas. Todas são opcionais.

logo

Arte do cabeçalho, quando o modal representa outra marca que não a do site. Sem ela, o logo do site.

title

Título próprio no lugar do texto padrão do fluxo.

pending

Trava os campos e troca o rótulo do botão enquanto a sua chamada corre.

error

Mensagem do servidor, exibida em alerta acima do formulário. Quem a limpa é quem a passou.

sent

Só no de recuperação: troca o formulário pela confirmação de envio. É do chamador, porque quem sabe que o pedido chegou é quem o fez.

name / avatar

Só no de bloqueio: nome e retrato de quem está com a sessão travada.

Ver os cinco funcionando

Formulários e collapses

Campos sobre a tinta da lente e o details nativo de cartão.

es-input serve input e textarea; es-select é o select nativo com a mesma tinta. O es-collapse veste o details do navegador: abre, fecha e é acessível sem uma linha de JS.

<echo-input v-model="email" type="email" />

<echo-textarea v-model="message" rows="3" />

<echo-select v-model="topic">…</echo-select>

<echo-collapse>
  <template #summary>…</template>
  …
</echo-collapse>
Ver na vitrine

Barras, rodapés e trilhos

O chrome dos quatro tipos de site que o grupo constrói, pronto para vestir.

A forma do chrome é a primeira decisão de um site derivado e a mais cara de desfazer depois: uma loja precisa da busca no meio da primeira faixa, um curso do progresso do leitor, uma central do estado do serviço e uma landing page de um CTA sem concorrência. Partir da mais próxima das quatro sai mais barato que dobrar a barra genérica.

<masthead-ecommerce
  :links="LINKS"
  :departments="DEPARTMENTS"
  :cart-count="cart.length"
  flat
  @search="goToListing"
/>

<footer-ecommerce :columns="COLUMNS" :legal="LEGAL" />

As cinco barras e os cinco rodapés seguem o mesmo contrato, e é ele que permite trocar de variante sem reescrever a página.

Quatro tipos, não um tema

Loja, curso, central de ajuda e landing page. Não são skins da mesma barra: o que muda é o que ocupa o meio da primeira faixa.

Os rótulos entram por prop

Nenhuma variante guarda texto: os links, os departamentos e as colunas chegam prontos e traduzidos. Uma barra com nomes escritos dentro é uma barra que se edita em vez de usar.

Duas skins, uma decisão

O padrão é sem cápsula; `lens` a traz de volta, para a barra sobre arte. Os botões de ícone e os menus mudam juntos (`useBarSkin`): misturar as duas é pior que qualquer uma.

embedded para amostras

Tira o `sticky` e nada mais, para montar a barra dentro de uma página. É o que faz as amostras da vitrine serem as barras de verdade, não desenhos delas.

Regras

As convenções que valem para todo site derivado.

CSS num lugar só

Nenhum bloco style em .vue: utility no template e o resto no main.css.

Token para todo valor tematizável

Hex cru em template é achado; a marca inteira vive no bloco MARCA.

Prefixo es-

Toda classe componente e token do sistema usa o prefixo, renomeável num replace global.

Contraste nos dois temas

Todo texto verificado contra o campo real; cor de acento não é cor de texto.

Movimento e transparência

prefers-reduced-motion e prefers-reduced-transparency respeitados por toda animação e vidro.

Locales em lockstep

Espanhol canônico; toda string de interface via t(), contratos ficam literais.

Guia para agentes de IA

Um documento baixável com o sistema inteiro, escrito para ser entregue a um agente junto com a tarefa.

Nele estão as regras que valem antes de qualquer linha (cor só por token, dois eixos nunca combinados à mão, elemento nativo primeiro, texto por t(), componente que não busca dados) e o catálogo de todas as famílias com tag, props, eventos e trechos copiáveis ipsis litteris.

Cada página da vitrine tem também a sua versão, com a família daquela página apenas: é o arquivo a entregar quando a tarefa é uma tela, não um site.

/agents/echosistema-ui.md      # the whole system
/agents/buttons.md             # one family per file
/agents/navbars.md
/agents/calendar.md             …

Ajuda

Dúvida que a documentação não cobre?

Fale com o grupo EchoSistema em echosistema.online.

Changelog

O que mudou no template, do mais recente para trás.

  • Largura do tema: A régua do conteúdo passou de dois para três valores (compact, extended, full), com o padrão do deploy vindo de NUXT_THEME_WIDTH / NUXT__THEME_WIDTH e assado no script de pré-pintura.
  • Barras, rodapés e trilhos: Cinco mastheads e cinco rodapés (loja, portal imobiliário, curso, central de ajuda, landing), três barras laterais e a página de seletores, com prévia de celular em viewport real.
  • Conta em duas formas: Os campos saíram dos modais para componentes próprios, e as quatro rotinas ganharam rotas de verdade, sem masthead, no cartão centrado ou na divisão com imagem em 60% da tela.
  • Calendário: Agenda em quatro visões, mês pequeno para o trilho e a aritmética de datas em utilitários - sem dependência nova, sobre os tokens do tema.
  • Vitrine redistribuída: Botões e rótulos em páginas separadas, grupo de autenticação, grupo de navegação com o chrome e uma página de formulários completos.
  • Uma origem de API: A Referência publica a origem do deploy e nada mais, e o painel de teste dispara contra ela: o seletor de servidor era uma segunda fonte de verdade.
  • Guia para agentes: Documentos baixáveis em /agentes: o sistema inteiro num arquivo e uma versão por página da vitrine.

  • Vitrine dividida: A página única de oitocentas linhas virou seis rotas com menu lateral, uma por assunto.
  • Navegação: Paginação, trilha, abas com tablist da ARIA, linha do tempo e gaveta lateral.
  • Primitivos de painel: Barra de progresso, anel e linha de estatística; os cartões de painel viraram composição deles.
  • Cartão e lista: Card com cabeçalho, mídia e as quatro ações (recolher, atualizar, expandir, remover), mais List em três densidades.
  • Media player: Plyr como primeira dependência de runtime, por causa de Vimeo e YouTube atrás de uma interface só.
  • Toasts: Fila global com posição no estado, e a mesma cunha de acento dos alerts e modais.
  • Avaliação: Leitura com fração e interativa sobre rádios nativos, com as setas do teclado de graça.
  • Controles e escala: Toggler, caixa de marcar, rádio, seletores nativos de data e cor, e a escala sm/md/lg dos botões.
  • Sistema de intenções: Dez intenções com variantes para botões, chips, badges e pills, em dois eixos de classes.
  • Alerts em oito tons: Os quatro tonais nomeados entram na paleta, com a cunha arredondada de acento.
  • Modais nativos: Dialog opaco com cunha de intenção, fundo embaçado, escala de largura e rolagem travada.
  • Vitrine e tipografia: Páginas /componentes e /tipografia com todos os elementos do sistema.
  • Três famílias: Manrope, Montserrat e JetBrains Mono, cada uma com o seu papel fixo no sistema.
EchoSistema TemplateVoltar ao site

A base visual dos sites de plataforma do grupo EchoSistema.