Documentation index

Template documentation

The EchoSistema starter guide: how to run it, where everything lives and how to use the system's tokens and components.

Introduction

The Nuxt starter for the group's platform sites: the whole Liquid Glass parameterized by four brand tokens.

Brand tokens

Four hex values and one gradient derive all the CSS through color-mix; swapping the palette means editing one block.

Light and dark themes

Automatic parity: every themeable value has a dark twin, and the right theme paints before first paint.

Three languages

Canonical Spanish at the root, pt-BR and English prefixed, a single catalog and locales in lockstep.

Native components

The browser's dialog, details and selects dressed by the system: accessibility for free.

Quick start

pnpm installs, runs and validates. The same scripts as package.json, no hidden steps.

CommandRole
pnpm devDevelopment server with hot reload.
pnpm buildProduction build (SSR).
pnpm generatePrerendered static site.
pnpm previewServes the production build locally.
pnpm lintESLint across the whole project.
pnpm typecheckvue-tsc over the app and the configuration.

Structure

Standard Nuxt 4 layout: app/ is the site, i18n/ the texts, shared/ what server and client share.

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

main.css is the single style entry point: .vue files load no style blocks. The language catalog lives in shared/languages.ts and the navigation in app/data/nav.ts.

Brand tokens

The MARCA block at the top of main.css is the only place with brand hex values.

Backgrounds, text, glass, buttons and the code panel all derive from these four tokens through color-mix, with automatic light/dark parity. To rebrand a derived site, swap this block and nothing else; the agro and consumer palettes sit commented in the file as examples.

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

Theme and languages

The theme flips on html[data-theme]; the language owns the URL.

data-theme

A pre-paint script in the head reads the stored preference (or the system's) and sets data-theme before first paint: no flash of the wrong theme. Runtime switching is the same attribute.

prefix_except_default

Canonical Spanish lives unprefixed; /pt-BR and /en prefix the other routes. Adding a language is one entry in the shared/languages.ts catalog plus the matching locale file.

Typography

Semantic content is dressed by its container: .es-prose styles the elements inside it.

Headings, paragraphs, quotations, code and the other text elements pick up the system's style by entering an .es-prose, with no per-element utilities. The loaded families are Manrope, Montserrat and JetBrains Mono.

<div class="es-prose">
  <h2>…</h2>
  <p>…</p>
  <blockquote>…</blockquote>
</div>
See the typography reference

Inventory

The 41 components in app/components/echo, by role. Each dresses a native element when one exists: that is where keyboard, focus and announcement come from, with no reimplementation.

Actions and navigation

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

Forms

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

Feedback and waiting

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

Content

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

Media and codes

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

Intents and variants

Two axes never combined by hand: .es-i--* provides the color, the variant consumes it.

Ten intents define local tokens (--i, --i-on, --i-grad and derivatives) consumed by the four label families and the modal. The variants (filled, gradient, outline, glass, ghost) are each written once.

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

Eight tones over the same palette, with the accent wedge on the edge.

Four semantic status tones (info, success, warn, danger) and four named tonal ones (caution, steel-blue, navy-blue, dark). Text stays in the theme's grays; the accent goes into the icon, the title and the wedge.

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

Modals

The native dialog: trapped focus, Esc, inert and blurred background.

An opaque theme surface, an intent wedge like the alerts, a background blurred into illegibility while open and locked scrolling. Clicking outside does not close; Esc, the header close and the footer actions do. Width follows the sm, md, lg, xl and xxl scale.

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

modal.value?.open()

How to use each one

The shortest line that puts each component on screen, ready to copy.

Every component carries the same snippet in its own file, in a "Copy and paste" block right above the code — that is where the full prop list lives. What is here are the minimum usages, to find what fits without opening seven files.

Actions and navigation

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

Forms

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

Feedback and waiting

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

Content

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

Media and codes

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

Account modals

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

Account modals

Sign in, create account, recover and change the password, and the locked session. Five ready components in app/components/auth/, over the same echo-modal.

All five follow the same contract: open() and close() exposed by ref, one submit with the data, and no network calls inside. The application knows the URL, the token and what to do with the response — the component only collects, validates what doesn't depend on the server, and hands it back.

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

The submit comes out in the API's vocabulary, not the internal variables': sending the payload straight to the endpoint is the common case, and renaming fields in every derived site would be the same mapping written many times.

Account modal contract
ComponentSubmit fieldsEvents
<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

The props are the same across all five, with two noted exceptions. All are optional.

logo

Header artwork, when the modal stands for a brand other than the site's. Without it, the site logo.

title

Your own title instead of the flow's default text.

pending

Locks the fields and swaps the button label while your call runs.

error

Server message, shown in an alert above the form. Whoever passed it clears it.

sent

Recovery modal only: swaps the form for the sent confirmation. It belongs to the caller, because whoever made the request is who knows it arrived.

name / avatar

Lock modal only: name and portrait of whoever's session is locked.

See all five running

Forms and collapses

Fields over the lens ink and the native details as a card.

es-input covers input and textarea; es-select is the native select with the same ink. es-collapse dresses the browser's details: it opens, closes and stays accessible without a line of 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>
See it in the showcase

Rules

The conventions that hold for every derived site.

CSS in one place

No style blocks in .vue: utilities in the template and everything else in main.css.

A token for every themeable value

Raw hex in a template is a finding; the whole brand lives in the MARCA block.

The es- prefix

Every system component class and token uses the prefix, renameable in one global replace.

Contrast in both themes

All text checked against the real field; accent color is not text color.

Motion and transparency

prefers-reduced-motion and prefers-reduced-transparency respected by every animation and every glass.

Locales in lockstep

Canonical Spanish; every interface string through t(), contracts stay literal.

Help

A question the documentation does not cover?

Talk to the EchoSistema group at echosistema.online.

Changelog

What changed in the template, most recent first.

  • Split showcase: The single eight-hundred-line page became six routes with a sidebar, one per subject.
  • Navigation: Pagination, breadcrumb, tabs with a real ARIA tablist, timeline and drawer.
  • Dashboard primitives: Progress bar, ring and stat row; dashboard cards became composition of those.
  • Card and list: Card with header, media and the four actions (collapse, refresh, expand, remove), plus List in three densities.
  • Media player: Plyr as the first runtime dependency, for Vimeo and YouTube behind a single interface.
  • Toasts: A global queue with the position in state, and the same accent wedge as alerts and modals.
  • Rating: Read-only with fractions and interactive over native radios, with keyboard arrows for free.
  • Controls and scale: Toggler, checkbox, radio, native date and color pickers, and the sm/md/lg button scale.
  • Intent system: Ten intents with variants for buttons, chips, badges and pills, on two class axes.
  • Alerts in eight tones: The four named tonal ones join the palette, with the rounded accent wedge.
  • Native modals: Opaque dialog with an intent wedge, blurred background, width scale and locked scrolling.
  • Showcase and typography: The /componentes and /tipografia pages with every element of the system.
  • Three families: Manrope, Montserrat and JetBrains Mono, each with its fixed role in the system.
EchoSistema TemplateBack to the site

The visual foundation of the EchoSistema group's platform sites.