Skip to content

Global Styles & Design Tokens

Your plugin's Vue components are loaded into the same page as the rest of EA, so they inherit all of the app's global CSS automatically — no imports needed. This page documents what's actually available: color/spacing tokens, ready-made classes for common page shapes, and a couple of conventions worth copying even though they aren't shipped as global classes yet.

Design Tokens (CSS Custom Properties)

EA's active PrimeVue theme defines these as CSS custom properties on :root. Use them in your own <style> blocks (or inline styles) instead of hardcoding colors, so your plugin stays correct if the user switches theme:

TokenDefault valueUse for
--surface-ground#eff3f8Page background
--surface-card#ffffffCards, panels, chrome
--surface-hover#f6f9fcRow/item hover background
--surface-border#dfe7efHairline borders
--text-color#495057Primary text
--text-color-secondary#6c757dSecondary/muted text
--primary-color#6366f1The one accent color — don't introduce a second hue for emphasis
--highlight-bgtheme-dependentActive/selected row background
--highlight-text-colortheme-dependentText on --highlight-bg
vue
<style scoped>
.my-panel {
  background: var(--surface-card);
  border: 1px solid var(--surface-border);
  color: var(--text-color);
}
.my-panel:hover {
  background: var(--surface-hover);
}
</style>

These are runtime CSS variables, safe to reference from any plugin component. Don't reach for SCSS $variables (like $borderRadius) — those are compiled into the app's own bundle at build time and aren't available to a dynamically-loaded plugin SFC.

Icons & Utility Classes

Two icon sets and PrimeFlex's utility classes (flex, grid, col-*, gap-2, w-full, spacing/text-color helpers, etc.) are globally available — see the Icons and PrimeFlex Reference sections of the Vue UI Guide for the full tables.

Ready-to-Use Global Classes

These classes are defined in the app's own global stylesheet — reference them directly in your plugin templates without redefining anything.

Page header.page-header, .page-header__left, .page-header__identity, .page-header__icon-badge, .page-header__title, .page-header__subtitle, .page-header__actions, .icon-only-btn, .back-btn

List/table pages.table-card, .table-toolbar, .table-toolbar__left, .table-toolbar__right, .table-title, .search-wrap / .search-input, .toolbar-count, .clean-table (apply to <DataTable class="clean-table">), .row-actions, .action-btn (add --danger, --success, or --warning for hover-tinted variants), .empty-state, .empty-state__icon-badge, .empty-state__title, .empty-state__subtitle

Entrance animation.page-enter (subtle fade-in-up on mount; add to your page's root element)

Example: a typical list page

vue
<template>
  <div class="page-enter">
    <div class="page-header">
      <div class="page-header__left">
        <div class="page-header__identity">
          <div class="page-header__icon-badge"><i class="pi pi-music"></i></div>
          <div>
            <div class="page-header__title">Playlists</div>
            <div class="page-header__subtitle">Manage your plugin's playlists</div>
          </div>
        </div>
      </div>
      <div class="page-header__actions">
        <Button icon="pi pi-plus" label="New" @click="open('add')" />
      </div>
    </div>

    <div class="table-card">
      <div class="table-toolbar">
        <div class="table-toolbar__left">
          <span class="table-title">All Playlists</span>
          <span class="toolbar-count">{{ items.length }}</span>
        </div>
        <div class="table-toolbar__right search-wrap">
          <InputText v-model="q" class="search-input" placeholder="Search..." />
        </div>
      </div>

      <DataTable v-if="items.length" :value="items" class="clean-table">
        <Column field="name" header="Name" />
        <Column header="Actions">
          <template #body="{ data }">
            <div class="row-actions">
              <button class="action-btn" @click="open(data.id)"><i class="pi pi-pencil"></i></button>
              <button class="action-btn action-btn--danger" @click="remove(data)"><i class="pi pi-trash"></i></button>
            </div>
          </template>
        </Column>
      </DataTable>

      <div v-else class="empty-state">
        <div class="empty-state__icon-badge"><i class="pi pi-inbox"></i></div>
        <div class="empty-state__title">No playlists yet</div>
        <div class="empty-state__subtitle">Create one to get started</div>
      </div>
    </div>
  </div>
</template>

Form Page Pattern (copy, don't assume it's global)

Unlike the classes above, .form-card, .identity-strip, .form-section, and .field labels are not yet shipped as reusable global CSS — today they exist only as scoped styles inside a couple of the app's own pages. Copy the pattern into your own component's <style scoped> block rather than relying on it being available:

vue
<style scoped>
.form-card { background: var(--surface-card); border: 1px solid var(--surface-border); border-radius: 12px; padding: 1.5rem; }
.form-section { margin-bottom: 1.5rem; }
.form-section__label { font-weight: 600; color: var(--text-color); margin-bottom: 0.75rem; display: block; }
.field { margin-bottom: 1rem; }
</style>

Things to Avoid

To keep plugin UI visually consistent with the rest of EA:

  • No gradients, glassmorphism, or glow/neon shadows
  • No hover-lift transforms on cards or rows
  • No pill-shaped buttons — match the app's existing border-radius scale
  • No oversized border-radius on cards/panels
  • No per-item rainbow colors — --primary-color is the one accent; use severity colors (success/warn/danger) sparingly and consistently
  • Use :focus-visible for keyboard-focus styling, not bare :focus