Now.js Framework Documentation

Now.js Framework Documentation

AppConfigManager

EN 11 Feb 2026 07:24

AppConfigManager

Overview

AppConfigManager is the theme and site configuration management system in Now.js Framework. It supports:

  • Theme Management: Light/Dark mode switching with system preference detection
  • Site Metadata: Manage title, description, logo, contact, social information
  • CSS Variables: Load and manage CSS custom properties from backend
  • Auto-enhanced components: Toggle buttons (data-component="theme" or data-component="config")
  • data-on-load: Execute scripts after config is loaded
  • Smooth transitions: Anti-FOUC with fade animations
  • Backend API integration: Load both theme + site metadata in a single request

Basic Usage

Initialization

await Now.init({
  config: {
    enabled: true,
    defaultTheme: 'light',
    storageKey: 'app_theme',
    systemPreference: true,

    // Load theme + site metadata from API
    api: {
      enabled: true,
      configUrl: '/api/config',
      cacheResponse: true
    }
  }
});

API Response Format

{
  "variables": {
    "--color-primary": "#29336b",
    "--logo-url": "/images/logo.png"
  },
  "site": {
    "title": "Bookstore",
    "description": "Old & Rare Books",
    "logo": "/images/logo.png",
    "favicon": "/images/favicon.ico",
    "lang": "en",
    "contact": {
      "email": "info@example.com",
      "phone": "+1-xxx-xxxx",
      "address": "123 Main St"
    },
    "social": {
      "facebook": "https://facebook.com/...",
      "line": "@example",
      "twitter": "@example"
    }
  }
}

Toggle Theme

AppConfigManager.toggle();               // Switch light ↔ dark
await AppConfigManager.setTheme('dark'); // Set specific theme
const theme = AppConfigManager.getCurrentTheme();

Site Metadata Management

Get Site Metadata

// Get all metadata
const site = AppConfigManager.getSiteMetadata();
console.log(site.title);        // 'Bookstore'
console.log(site.description);  // 'Old & Rare Books'

// Get specific value (dot notation supported)
const title = AppConfigManager.getSite('title');
const email = AppConfigManager.getSite('contact.email');
const facebook = AppConfigManager.getSite('social.facebook');

// Use via window.siteConfig (auto-exported)
console.log(window.siteConfig.title);
console.log(window.siteConfig.contact.email);

Update Site Metadata

// Apply new metadata (replace all)
AppConfigManager.applySiteMetadata({
  title: 'New Title',
  description: 'New description',
  logo: '/new-logo.png'
});

// Partial update (merge with existing)
AppConfigManager.updateSiteMetadata({
  title: 'New Title',
  contact: {
    phone: '+1-999-9999'  // Update phone only
  }
});

Use in Templates

<!-- Use via window.siteConfig -->
<header>
  <img data-attr="src:siteConfig.logo" alt="Logo">
  <h1 data-text="siteConfig.title"></h1>
  <p data-text="siteConfig.description"></p>
</header>

<footer>
  <p>Email: <span data-text="siteConfig.contact.email"></span></p>
  <p>Tel: <span data-text="siteConfig.contact.phone"></span></p>
  <a data-attr="href:siteConfig.social.facebook">Facebook</a>
  <a data-attr="href:siteConfig.social.twitter">Twitter</a>
</footer>

data-on-load Support

Execute script after config is loaded (similar to ApiComponent):

<body data-on-load="console.log('Config loaded!', site, theme, config)">
  <!--
    Available variables:
    - site: site metadata object
    - theme: current theme ('light' or 'dark')
    - config: full config { site, theme, variables }
  -->
</body>

Example usage:

<body data-on-load="
  document.title = site.title;
  updateHeaderLogo(site.logo);
  initSocialLinks(site.social);
">

Toggle Button

HTML

<!-- Auto-enhanced - no JS required -->
<button data-component="theme">🌓</button>

<!-- Or use new name -->
<button data-component="config">🌓</button>

AppConfigManager will auto-enhance the button:

  • ✅ Adds click handler
  • ✅ Syncs data-theme-state attribute
  • ✅ All buttons sync when theme changes

CSS for Toggle Button

/* Icon font setup */
[data-component="theme"]:before,
[data-component="config"]:before {
  font-family: 'icomoon' !important;
}

/* Show icon based on theme */
[data-component="theme"][data-theme-state="light"]:before,
[data-component="config"][data-theme-state="light"]:before {
  content: "\e929";  /* icon-night */
}

[data-component="theme"][data-theme-state="dark"]:before,
[data-component="config"][data-theme-state="dark"]:before {
  content: "\e9d4";  /* icon-day */
}

Or use CSS classes:

[data-theme-state="light"] .icon-light { display: none; }
[data-theme-state="dark"] .icon-dark { display: none; }
  • ✅ All buttons sync when theme changes

CSS for Toggle Button

/* Icon font setup */
[data-component="theme"]:before {
  font-family: 'icomoon' !important;
}

/* Show icon based on theme */
[data-component="theme"][data-theme-state="light"]:before {
  content: "\e929";  /* icon-night */
}

[data-component="theme"][data-theme-state="dark"]:before {
  content: "\e9d4";  /* icon-day */
}

Or use CSS classes:

[data-theme-state="light"] .icon-light { display: none; }
[data-theme-state="dark"] .icon-dark { display: none; }

Full Configuration

await Now.init({
  config: {  // or use 'theme' (backward compatible)
    enabled: true,
    defaultTheme: 'light',
    storageKey: 'app_theme',
    systemPreference: true,

    // Smooth Transitions
    transition: {
      enabled: true,
      duration: 300,
      hideOnSwitch: true,
      loadingClass: 'theme-loading',
      readyClass: 'theme-ready',
      transitionClass: 'theme-transitioning'
    },

    // Backend API - load theme + site metadata
    api: {
      enabled: true,
      configUrl: '/api/config',
      headers: {},
      timeout: 5000,
      cacheResponse: true
    }
  }
});

Backend API Integration

Load Config from API

// Auto-load on init (if api.enabled = true)
await Now.init({ config: { api: { enabled: true, configUrl: '/api/config' } } });

// Or load manually
await AppConfigManager.loadFromAPI('/api/config');

// Refresh (clear cache + reload)
await AppConfigManager.refreshFromAPI();

API Response Format

{
  "variables": {
    "--color-primary": "#6366f1",
    "--color-background": "#0f172a",
    "--logo-url": "/images/logo.png",
    "--hero-bg": "/images/hero.jpg"
  },
  "site": {
    "title": "My Site",
    "description": "Site description",
    "logo": "/images/logo.png",
    "favicon": "/images/favicon.ico",
    "lang": "en",
    "contact": {
      "email": "info@example.com",
      "phone": "+1-xxx-xxxx"
    },
    "social": {
      "facebook": "https://facebook.com/...",
      "twitter": "@example"
    }
  }
}

Note:

  • variables - CSS custom properties (optional)
  • site - Site metadata (optional)
  • API does NOT control light/dark mode - user controls that

CSS Variables

Apply Variables

AppConfigManager.applyVariables({
  '--color-primary': '#6366f1',
  '--color-background': '#0f172a',
  '--hero-bg': '/images/hero.jpg'  // Auto-wraps as url()
});

Security

  • Only CSS Custom Properties (--*) allowed
  • Dangerous patterns removed (javascript:, expression(), <script>)
  • URLs: local paths or same-origin absolute URLs allowed
  • External domains blocked by default
  • Extensions must be images (no SVG)

Smooth Transitions

Required CSS

body {
  opacity: 0;
  transition: opacity 0.3s ease;
}

body.theme-ready {
  opacity: 1;
}

body.theme-transitioning {
  opacity: 0;
}

How It Works

  1. body starts with opacity: 0
  2. AppConfigManager loads theme
  3. Adds theme-ready class → fade in
  4. On switch → fade out → apply → fade in

API Reference

Theme Methods

Method Description
toggle() Toggle theme (light ↔ dark)
setTheme(theme) Set theme ('light' or 'dark')
getCurrentTheme() Get current theme

Site Metadata Methods

Method Description
getSiteMetadata() Get all site metadata
getSite(key) Get specific value (dot notation supported)
applySiteMetadata(data) Apply new metadata
updateSiteMetadata(updates) Partial update (merge)
exportToWindow() Export to window.siteConfig

CSS Variables Methods

Method Description
applyVariables(vars) Apply CSS Variables
clearVariables() Remove CSS Variables
getAppliedVariables() Get applied variables

API Methods

Method Description
loadFromAPI(url?) Load from API
refreshFromAPI() Reload (clear cache)

Lifecycle Methods

Method Description
init(options) Initialize manager
destroy() Full cleanup
reset() Reset state

Events

Theme Events

Event When Triggered Payload
theme:initialized Init complete { theme }
theme:changed Theme changed { theme }
theme:ready Ready to display -

Config/Site Events

Event When Triggered Payload
config:site-loaded Site metadata loaded { site }
config:site-updated Metadata updated { site, updates }
config:exported Exported to window { siteConfig }
config:on-load-executed data-on-load executed { body, script }
config:on-load-error data-on-load error { error, script }

Variables Events

Event When Triggered Payload
theme:variables-applied Variables applied { variables }
theme:variables-cleared Variables cleared -

API Events

Event When Triggered Payload
theme:api-loaded API load success { config, fromCache }
theme:api-error API error { error, url }

Lifecycle Events

Event When Triggered
theme:destroyed Cleanup complete
theme:reset Reset complete
// Usage examples
EventManager.on('theme:changed', ({ theme }) => {
  console.log('Changed to:', theme);
});

EventManager.on('config:site-loaded', ({ site }) => {
  console.log('Site loaded:', site.title);
  document.title = site.title;
});

EventManager.on('config:site-updated', ({ site, updates }) => {
  console.log('Updated:', updates);
});

Cleanup

// Full cleanup (unmount app)
AppConfigManager.destroy();

// Light reset (re-init)
AppConfigManager.reset();
await AppConfigManager.init({ ... });

Common Pitfalls

⚠️ 1. Enable Before Use

// ❌ Not enabled
AppConfigManager.toggle();

// ✅ Enable first
await Now.init({ config: { enabled: true } });
AppConfigManager.toggle();

⚠️ 2. Use CSS Variables

/* ❌ Using fixed values */
body { background: white; }

/* ✅ Use variables */
body { background: var(--color-background); }

⚠️ 3. API Response Format

// ✅ Correct - site is an object
{
  "variables": {...},
  "site": { "title": "...", "contact": {...} }
}

// ❌ Wrong - site is an array
{
  "site": [...]
}

⚠️ 4. Global vs Page-specific Data

// ✅ Global data → AppConfigManager
// - Site title, logo, description
// - Contact info, social links
// - Theme CSS variables

// ✅ Page-specific data → apiComponent
// - Product list/detail
// - User profile
// - Order history

⚠️ 5. Use HTTP/HTTPS Protocol

// ❌ file:// protocol can't use API
file:///path/to/index.html

// ✅ http(s):// protocol
http://localhost/...
https://example.com/...

⚠️ 6. Backward Compatibility

// ✅ Both work
window.AppConfigManager
window.ThemeManager  // Alias

Now.init({ config: {...} })  // Recommended
Now.init({ theme: {...} })   // Still works

Use Cases

1. Multi-language Site

const lang = AppConfigManager.getSite('lang');  // 'en' or 'th'

// Change language
AppConfigManager.updateSiteMetadata({
  lang: 'en',
  title: 'Bookstore',
  description: 'Old & Rare Books'
});

2. Dynamic SEO

EventManager.on('config:site-loaded', ({ site }) => {
  // Auto-update on load
  document.title = site.title;

  const metaDesc = document.querySelector('meta[name="description"]');
  if (metaDesc) metaDesc.content = site.description;
});

3. White-label / Multi-tenant

// Different config per tenant
await AppConfigManager.init({
  api: {
    enabled: true,
    configUrl: `/api/config?tenant=${tenantId}`
  }
});

// tenant A: logo A, colors A, contact A
// tenant B: logo B, colors B, contact B

4. Theme-based Branding

EventManager.on('theme:changed', ({ theme }) => {
  const logo = theme === 'dark'
    ? '/images/logo-dark.png'
    : '/images/logo-light.png';

  AppConfigManager.updateSiteMetadata({ logo });
});

Performance Benefits

Before (Separate APIs)

/api/theme → 150ms (CSS variables)
/api/site/info → 120ms (header)
/api/site/info → 120ms (footer, cached)
────────────────────────────────
Total: 270ms + 2-3 HTTP requests

After (Combined API)

/api/config → 180ms (theme + site)
────────────────────────────────
Total: 180ms + 1 HTTP request
Savings: 90ms + 1-2 requests ⚡