Skip to content

Storefront templates and branding ​

How the public client portal picks a design, how to add one, and which settings brand it.

The public portal ships three designs and renders whichever one is selected. All produce identical SEO output — canonical, hreflang and schema.org live in client/pages/, never in a template.

Set the site's own origin, or the sitemap will advertise whatever host each request happened to arrive on — behind a proxy that can be the container's internal address. NUXT_PUBLIC_BASE_URL covers it at build time; a prebuilt image reads NUXT_SITE_URL at startup, which overrides the built-in value.

TemplateDescription
auroraDefault. Dark and light modes, Armenian typography, live domain search
novaConversion-focused hosting landing page: pricing, plan comparison, security, migration, FAQ
classicThe original storefront design

nova supplies its own header, footer, homepage and hosting page, and reuses aurora's domain search and checkout — the checkout is the ordering path, and a second copy of it would be a second place for a payment bug to live. Its design tokens are prefixed --n-* and declared only under .tpl-nova and html[data-template='nova'], so activating it cannot disturb the other two.

Several of its cards state things this codebase cannot verify — NVMe storage, a CDN, server-side caching, a firewall, a malware scanner, a backup schedule, an uptime figure, free migration. Each waits on a setting and is hidden until an operator turns it on:

SettingShows
portal.features.storagethe SSD/NVMe storage card
portal.features.cachingthe server-side caching card
portal.features.cdnthe CDN card
portal.features.firewallthe firewall card
portal.features.malwarethe malware-protection card
portal.features.backupsthe backup cards, in the hero, trust bar and security grid
portal.trust.uptimethe uptime figure in the trust bar, printed verbatim
portal.migration.freethe "free migration" badge, when set to true

Any non-empty value turns the first six on; the uptime setting holds the figure itself. The comparison table and the testimonials section are data-driven the same way: the table renders only the specification lines entered under Admin → Products → Specification, and the testimonials section does not render at all, because nothing serves customer quotes yet.

Choosing one. In order of precedence:

  1. The portal.template setting, editable in Admin → Settings → Portal appearance
  2. The NUXT_PUBLIC_PORTAL_TEMPLATE environment variable
  3. classic (DEFAULT_TEMPLATE in client/templates/types.ts)

An unrecognised value falls back to classic rather than leaving the site blank, so a typo in the admin field cannot take the storefront down.

Adding a template. Create client/templates/<name>/ with layout/Header.vue, layout/Footer.vue and a pages/ component for each route, then register the loaders in client/templates/registry.ts and add the name to TEMPLATE_NAMES in client/templates/types.ts. A unit test asserts every template implements every slot, so a missing page fails the build rather than rendering nothing.

Two rules make templates safe to swap:

  • Templates render, pages decide. A template component takes typed props and returns markup; the page above it owns fetching, SEO and business logic. That keeps both designs on one code path and one set of head tags.
  • Copy lives in i18n under a top-level key named for the template (aurora.hero.title), registered in the modules array of client/plugins/i18n.ts. Locale files are not auto-discovered.

aurora follows the first rule throughout, with one exception: its pages/Checkout.vue still carries the ordering flow, because splitting a working payment path is only worth doing with a backend on hand to place a test order against. It is classic's script byte-for-byte with restyled markup, so the two cannot drift apart.

classic's pages are lift-and-shifts of the original route components and keep their own data loading. That was the point: moving a 500-line page unchanged is far safer than rewriting it, and the design it renders is the one being replaced. New templates should follow aurora, not classic.

Branding and colour mode ​

Branding and colour mode, all from Admin → Settings with a NUXT_PUBLIC_PORTAL_* environment fallback for a first boot (the admin value wins once set):

SettingEnv fallbackWhat it does
portal.site.nameNUXT_PUBLIC_PORTAL_SITE_NAMESite name for og:site_name, logo alt text and structured data. Empty shows the built-in name.
portal.site.taglineNUXT_PUBLIC_PORTAL_SITE_TAGLINEDefault meta description.
portal.theme.defaultNUXT_PUBLIC_PORTAL_THEME_DEFAULTlight, dark or system for a visitor with no saved choice. A visitor's own choice is kept in a color-mode cookie and rendered on the server, so there is no flash of the wrong theme.
portal.theme.user_toggleNUXT_PUBLIC_PORTAL_THEME_USER_TOGGLEfalse hides the light/dark switch.
portal.logoNUXT_PUBLIC_PORTAL_LOGOLogo for light backgrounds.
portal.logo.darkNUXT_PUBLIC_PORTAL_LOGO_DARKLogo for dark backgrounds; falls back to the light one.
portal.logo.markNUXT_PUBLIC_PORTAL_LOGO_MARKSquare icon-only logo for compact places.
portal.faviconNUXT_PUBLIC_PORTAL_FAVICONBrowser tab icon; an upload generates the whole icon set.
portal.brand.primaryNUXT_PUBLIC_PORTAL_BRAND_PRIMARYBrand colour as #rrggbb. The whole primary scale — buttons, links, highlights, e-mail buttons — is derived from it; the text on it turns dark automatically when the colour is too light for white.
portal.brand.accentNUXT_PUBLIC_PORTAL_BRAND_ACCENTAccent colour as #rrggbb; gradients, badges and secondary highlights.
portal.brand.font_headingNUXT_PUBLIC_PORTAL_BRAND_FONT_HEADINGinter, noto-sans-armenian, noto-serif-armenian or system. Bundled; nothing loads from a font service.
portal.brand.font_bodyNUXT_PUBLIC_PORTAL_BRAND_FONT_BODYSame list, for body text.

Logos and the favicon can be uploaded in Admin → Settings → Branding or pasted as URLs. portal.template, portal.theme.default and the typefaces accept only their listed values, and the colours only #rrggbb; anything else is refused with INVALID_SETTING_VALUE and a sentence saying what was expected. With every brand setting empty the storefront renders each template's own colours and typefaces, exactly as before the settings existed.

Analytics and chat are settings too, empty by default, and nothing loads until they are set:

SettingEnv fallbackWhat it does
portal.analytics.gtm_idNUXT_PUBLIC_PORTAL_GTM_IDGoogle Tag Manager container (GTM-…). Loads only after a visitor accepts all cookies in the banner.
portal.chat.providerNUXT_PUBLIC_PORTAL_CHAT_PROVIDERchatwoot, innochat, or empty for no widget.
portal.chat.base_urlNUXT_PUBLIC_PORTAL_CHAT_BASE_URLThe chat server, https:// only.
portal.chat.website_tokenNUXT_PUBLIC_PORTAL_CHAT_WEBSITE_TOKENThe website token from the chat inbox. .ru / .hy variants override it per language.

E-mails follow the brand. Every template rendered by the API can use {{ site.name }}, {{ brand.primary }}, {{ brand.primary_dark }}, {{ brand.on_primary }}, {{ brand.accent }} and {{ brand.logo_url }} (the dark-background logo as an absolute URL, empty when none is uploaded). The password-reset template shipped with a fresh install uses them; an install that already has that row keeps its stored HTML until it is re-saved in Admin → Settings → Email Templates.

Related operator settings, all optional and hidden when empty: portal.contact.whatsapp, portal.contact.telegram, portal.contact.email, portal.contact.phone, portal.contact.address, portal.contact.map_url (https only), portal.stats.clients, portal.stats.uptime, portal.stats.regions, portal.hero.badge, portal.chat.provider, portal.newsletter.action_url, portal.legal.tax_id, portal.social.{facebook,instagram,twitter,linkedin,youtube}, and the support hours portal.contact.hours with their zone portal.contact.timezone.

The email, phone, address, map, hours and zone are all editable in Admin → Settings → Storefront; the email and the map address are checked when saved. The address and the hours can also be set per language with the .ru and .hy variants (portal.contact.address.ru, portal.contact.hours.hy, and so on); a language without its own value shows the default one.

The nova template states a hardware or commercial claim only when the operator switches it on (true; empty or false hides it): portal.features.{backups,storage,caching,cdn,firewall,malware} and portal.migration.free. The uptime line in its trust bar prints portal.stats.uptime.

The header app launcher is off unless portal.apps.enabled is true, because the apps it links to only exist in a deployment that runs them. Each entry then needs a URL of its own — portal.apps.{account,tasks,erp,hostpanel,sheets,mail,docs,calendar}, or the matching NUXT_PUBLIC_* variable — and entries without one are left out.

Every seeded setting is editable in Admin → Settings; the table takes one row at a time.

The plans page's comparison table is built from each product's specification lines, edited in Admin → Products → Specification. Plans line up by feature name, so the same line has to be spelled the same way across products; a plan that omits a line shows a dash rather than shifting its column. With no product specified, the table is not rendered at all.

Was this page helpful?

Released under the AGPL-3.0 License.