Skip to content
Twilight React Playground
ثيم رائدaren

Theme

interfaceBeginnerlive demo

Describes the merchant's theme record: colors, font, live or preview mode, and the settings object your twilight.json declares.

import { Theme, ThemeColor, ThemeFont, ThemeSettings } from '@salla.sa/twilight-theme-engine/types';

In plain words

Merchants customise a theme in the Salla dashboard. Theme is the shape of what they chose: color (the brand colors), font, and settings, the values of the settings your theme declares in twilight.json.

useTheme() gives a component color, font and settings; useTwilight().theme is the whole record. ThemeSettings lists only the settings the engine itself knows about, so a theme usually starts by telling TypeScript about its own settings, as the example shows.

Signature

interface Theme {
  name: string;
  mode: 'live' | 'preview' | string;
  is_rtl: boolean;
  color: ThemeColor;
  font?: ThemeFont;
  settings: ThemeSettings;
  id?: number;
  profile?: { id: number; name: string | null };   // the settings version the API answered with
  assets?: string;  bundle_assets?: string;
  customization?: { css?: string | null; js?: string | null };
  twilight?: { version?: string | null };
  side_menu_enabled?: boolean | null;  isDark?: boolean | null;
  components?: unknown[];  translations_hash?: string;
}

interface ThemeColor {
  primary: string;
  text: string;
  is_dark: boolean;
  reverse_primary: string;
  reverse_text: string;                 // the text color to use on primary
}

interface ThemeFont {
  name: string;
  id?: number;  path?: string;  url?: string;  type?: string | number;  family_name?: string;
}

interface ThemeSettings {               // the engine's known keys, every one optional
  font?: ThemeFont;  store_color?: string;  homepage_type?: string;
  header_is_sticky?: boolean;  header_layout?: 'lite' | 'default';  topnav_is_dark?: boolean;
  footer_is_dark?: boolean;  footer_layout?: 'columns' | 'centered';
  sticky_add_to_cart?: boolean;  enable_add_product_toast?: boolean;  enable_more_menu?: boolean;
  is_breadcrumbs_enabled?: boolean;  product_card_img_ratio?: string | { value?: string }[];
  // …30 keys in all
}

Try it live

The theme record this page was rendered with, field by field, against Theme and ThemeSettings. ✗ marks a value the type does not allow.Try this: pick theme.settings: is_custom_js reads undefined (declared, never sent), and the last line lists the keys sent but never declared.
Storefront canvas · en · LTR
theme.declaredsent by the demo store
idnumber | undefinedundefined
namestringstring "1298199463"
profileobject | undefinedobject {"id":2106733714,"name":null}
modestringstring "live"
is_rtlbooleanboolean false
colorobjectobject {"primary":"#ed1c24","text":"#000000","i
settingsobjectobject {"font":{"id":5,"url":"/fonts/dubai.css"
assetsstring | undefinedstring "https://cdn.assets.salla.network/themes
bundle_assetsstring | undefinedstring "https://cdn.assets.salla.network/themes
customizationobject | undefinedundefined
side_menu_enabledboolean | null | undefinednull null
twilightobject | undefinedobject {"version":"2.14.583"}
fontobject | undefinedobject {"id":5,"name":"Dubai","path":"/fonts/du
componentsarray | undefinedarray []
isDarkboolean | null | undefinednull null
translations_hashstring | undefinedstring "178990889138"

Sent but not in the type: nothing

Controls
Only mismatches
What a theme writes
import { useTwilight } from '@salla.sa/twilight-theme-engine';

export function PreviewRibbon() {
  const { theme } = useTwilight();
  // The engine gives <body> a preview-mode class for the same check.
  if (theme.mode !== 'preview') return null;
  return <div className="preview-ribbon">Preview</div>;
}

Example

app/types/theme-settings.d.ts
// app/types/theme-settings.d.ts
export {}; // keeps this file a module, so the block below extends the engine's type

declare module '@salla.sa/twilight-theme-engine/types' {
  interface ThemeSettings {
    show_promo_banner?: boolean; // the ids of your twilight.json settings
    promo_text?: string;
  }
}

// app/components/PromoBanner.tsx
import { useTheme } from '@salla.sa/twilight-theme-engine/hooks/useTheme';

export function PromoBanner() {
  const { color, settings } = useTheme();
  // A merchant who never saved the setting has no value: supply the default.
  if (!(settings.show_promo_banner ?? true)) return null;

  return (
    <div className="promo-banner" style={{ background: color.primary, color: color.reverse_text }}>
      {settings.promo_text ?? 'Free delivery'}
    </div>
  );
}

How it behaves

  • Read it with useTheme() (color, font, settings, and is_rtl renamed isRTL), useTwilight().theme, or getTwilightContext().settings.theme in a loader.

  • The engine applies the theme for you (ThemeDocumentSync, mounted by TwilightProvider). <body> gets salla-<name>, color-mode-dark or color-mode-light from color.is_dark, preview-mode when mode is preview, font-<name>, footer-is-dark or footer-is-light, topnav-is-dark and is-sticky-product-bar.

  • After hydration, applyTheme writes CSS variables on <html>: --color-primary with --color-primary-dark and --color-primary-light shades, --color-primary-reverse from reverse_text, --color-reverse-primary, and --font-main from font.name.

  • is_rtl follows the language the settings were requested in: true for ar and false for en on the demo store.

  • In development the dev settings widget can overlay settings with values it reads from your twilight.json (useTwilightInit). A production build never does.

  • profile.id is the version of the merchant's settings the API answered with; the engine sends it back as the s-version-id header on later API calls.

Gotchas

  • Your own twilight.json settings are not in ThemeSettings: settings.show_promo_banner fails with TS2339. Extend the interface with declare module, as in the example. useTheme().settings and useTwilight().theme.settings both pick the addition up.

  • The file holding the declare module block needs an import or export {}. Without one, the block declares a whole new module that replaces the engine's: every other export of @salla.sa/twilight-theme-engine/types, HookName included, stops resolving.

  • settings.font is not font. On the demo store settings.font has a relative url (/fonts/dubai.css), a numeric type and an Arabic name, while font has the absolute URL and the family name Dubai. useTheme().font and the CSS variables use font.

  • name is not a display name: the demo store sends "1298199463", and no id. And the engine never applies color.text: applyTheme sets --color-text to #222222 whatever the merchant chose, so use color.text yourself if your design needs it.

  • The keys drift from the API. The demo store sends is_custom_css, customization_css_ver and translations_hash inside settings, which the type does not declare, and never sends the declared is_custom_js and customization_js_ver.

  • docs/07-data-types.md shows settings as an accessor with get(key, default), and ThemeColor with darker() and lighter(). Neither exists: theme.settings.get(…) throws "is not a function". Read settings.key ?? fallback, and make shades with color() from @salla.sa/twilight-theme-engine/utils.

Related

Source and docs