Theme
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
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.| theme. | declared | sent by the demo store |
|---|---|---|
id | number | undefined | undefined |
name | string | string "1298199463" |
profile | object | undefined | object {"id":2106733714,"name":null} |
mode | string | string "live" |
is_rtl | boolean | boolean true |
color | object | object {"primary":"#ed1c24","text":"#000000","i |
settings | object | object {"font":{"id":5,"url":"/fonts/dubai.css" |
assets | string | undefined | string "https://cdn.assets.salla.network/themes |
bundle_assets | string | undefined | string "https://cdn.assets.salla.network/themes |
customization | object | undefined | undefined |
side_menu_enabled | boolean | null | undefined | null null |
twilight | object | undefined | object {"version":"2.14.583"} |
font | object | undefined | object {"id":5,"name":"Dubai","path":"/fonts/du |
components | array | undefined | array [] |
isDark | boolean | null | undefined | null null |
translations_hash | string | undefined | string "178990889138" |
Sent but not in the type: nothing
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
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, andis_rtlrenamedisRTL),useTwilight().theme, orgetTwilightContext().settings.themein a loader.The engine applies the theme for you (
ThemeDocumentSync, mounted byTwilightProvider).<body>getssalla-<name>,color-mode-darkorcolor-mode-lightfromcolor.is_dark,preview-modewhenmodeispreview,font-<name>,footer-is-darkorfooter-is-light,topnav-is-darkandis-sticky-product-bar.After hydration,
applyThemewrites CSS variables on<html>:--color-primarywith--color-primary-darkand--color-primary-lightshades,--color-primary-reversefromreverse_text,--color-reverse-primary, and--font-mainfromfont.name.is_rtlfollows the language the settings were requested in:trueforarandfalseforenon the demo store.In development the dev settings widget can overlay
settingswith values it reads from yourtwilight.json(useTwilightInit). A production build never does.profile.idis the version of the merchant's settings the API answered with; the engine sends it back as thes-version-idheader on later API calls.
Gotchas
Your own
twilight.jsonsettings are not inThemeSettings:settings.show_promo_bannerfails with TS2339. Extend the interface withdeclare module, as in the example.useTheme().settingsanduseTwilight().theme.settingsboth pick the addition up.The file holding the
declare moduleblock needs animportorexport {}. Without one, the block declares a whole new module that replaces the engine's: every other export of@salla.sa/twilight-theme-engine/types,HookNameincluded, stops resolving.settings.fontis notfont. On the demo storesettings.fonthas a relativeurl(/fonts/dubai.css), a numerictypeand an Arabicname, whilefonthas the absolute URL and the family nameDubai.useTheme().fontand the CSS variables usefont.nameis not a display name: the demo store sends"1298199463", and noid. And the engine never appliescolor.text:applyThemesets--color-textto#222222whatever the merchant chose, so usecolor.textyourself if your design needs it.The keys drift from the API. The demo store sends
is_custom_css,customization_css_verandtranslations_hashinsidesettings, which the type does not declare, and never sends the declaredis_custom_jsandcustomization_js_ver.docs/07-data-types.md shows
settingsas an accessor withget(key, default), andThemeColorwithdarker()andlighter(). Neither exists:theme.settings.get(…)throws "is not a function". Readsettings.key ?? fallback, and make shades withcolor()from@salla.sa/twilight-theme-engine/utils.
Related
Reads the merchant's theme colors, font, theme settings and whether the page reads right-to-left.
StoreDescribes the current store and its store-wide settings: name, logo, address, contacts, social accounts, apps and feature switches.
colorReads a six-digit hex color and returns a readable text color, a dark flag, and darker, lighter or inverted shades.
TwilightProviderThe component a theme mounts once around its pages; it shares the store, theme, language and navigation with everything inside it.