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.

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

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-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" />

Modais de conta

<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" />

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-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

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.

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.

  • 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.