Theme Reference
Theme Panel
Site settings page at WP Admin → Appearance → Theme Settings (always available).
Sections
- Admin Panel — Gutenberg toggle, admin bar, dashboard widgets
- Security — XML-RPC, REST API, version hiding, login URL
- Extensions — Forms handler, SMTP, Telegram, reCAPTCHA
- WooCommerce — WooCommerce-specific settings
- Theme Settings — Field backend (SCF/ACF), migration tools
- Search — Search page configuration
Queries
Build reusable WP_Query / get_terms() selections. Use them two ways: call a Query Preset from a PHP template, or attach a Custom Query to a loop element directly via HTML attributes.
From a PHP template (Query Presets)
Build a preset in the Post/Term Query Presets sub-tab, give it a Query Code, then call it from template code:
- Posts:
$q = new WP_Query(tf_get_query('your-code')); - Terms:
$terms = tf_get_term_query('your-code'); - Auto-apply: attach a preset to a post-type or taxonomy archive on the Auto-apply tab and it overrides the main query automatically (via pre_get_posts) — no template edit needed.
From HTML attributes (Custom Queries)
Name a query in the Custom Queries / Term Queries sub-tab, then reference it on the loop element — or write the arguments inline. Only the first child element is used as the repeating template.
- Named:
<div wp-select="catalog"> … </div>(alias:wp-query-name) - Inline:
<div wp-query="'post_type'=>'product','posts_per_page'=>8"> - Term list:
<div wp-term-query="'taxonomy'=>'product_cat','parent'=>0">·wp-term-select="name" - WooCommerce:
<div wp-product-query="get_related_products(4)">
Inside the loop — fields
On elements inside the loop, output values with WordPress attributes or ACF/SCF field attributes:
- WordPress:
wp="post-title",wp="post-image",wp="post-link",wp="term-title",wp="term-link" - ACF / SCF:
fld-text,fld-image,fld-link,fld-loop(Alternative format:f_text,f_image,f_loop)
Compatibility
The runtime reads three sources in priority order: TF presets, then Query Master (older imported sites), then the legacy configurator queries. New presets are always saved to TF storage; existing data keeps working.
Custom Post Types & Taxonomies
Register custom post types and taxonomies from the panel — each repeater row maps to a WordPress registration call, no code required.
Custom Post Types
Each row maps to register_post_type(). The Name field is the post-type key (lowercase Latin, no spaces). Set the plural/singular/menu labels, a Dashicons icon, the supported features (title, editor, thumbnail, …), the archive URL slug, REST/Gutenberg, public queryability, a Public archive toggle (turn the /slug/ feed off for service types) — and an optional Archive landing page that makes the archive editable (see the "Archive landing pages" section below).
Custom Taxonomies
Each row maps to register_taxonomy(). Choose Hierarchical (like categories) or Flat (like tags), link it to one or more post types, and set the slug and REST visibility.
How they connect
New post types and taxonomies appear automatically in the Query Presets dropdowns. Permalinks refresh on save. Templates follow WordPress conventions: single-{type}.php, archive-{type}.php, taxonomy-{taxonomy}.php.
Archive landing pages
An archive URL (the blog, the shop catalog, a custom post type archive) renders a LIST of posts — it has no page of its own, so normally there is nothing to edit on it. ThemeForge gives every archive a landing page: a real page from the Pages list whose fields become the archive's editable content (hero title, intro text, images, SEO), while the feed itself keeps rendering the list. Editors change the archive the same way they edit any other page.
How it works
On the archive page of your Webflow project (archive-post, archive-product, archive-{type}) place field attributes OUTSIDE the listing loop — that is, outside the element marked wp="posts" / wp="products". Those fields automatically attach to the landing page in the admin and render from it on the archive URL.
- Any single-value field works outside the loop: fld-text, fld-image, fld-link, fld-bg-image, fld-editor and the rest of the fld-* family — plus wp="post-title" and wp="post-content", which print the landing page's own title and content.
- Fields INSIDE the loop keep their normal meaning: they read each listed post, exactly as before.
- Multilingual sites resolve the landing page per language automatically (Polylang / WPML): each locale edits its own translation of the landing page.
- SEO: for custom post type archives the landing page also feeds Yoast — the archive gets the page's SEO title and a correct canonical URL, including paginated pages.
Blog
The blog landing is the native WordPress "Posts page". Create a page (for example /blog/), assign it in Settings → Reading, and the fields you placed outside the loop on the archive-post template attach to that page — no extra setup.
archive-postWooCommerce catalog
The shop catalog uses the WooCommerce Shop page as its landing. Fields placed outside the product loop on the archive-product template attach to the Shop page and render on the catalog URL.
archive-productCustom post types
A custom post type archive has no native landing in WordPress, so ThemeForge adds one: create a page whose URL matches the archive slug (for example /portfolio/ for a type with archive slug "portfolio"), then select it in the post type's row in the Theme Panel. The archive URL keeps rendering the feed template, and the selected page carries its editable fields. If the archive template in your Webflow project declares a fields group, ThemeForge creates (or adopts) the landing page automatically — in every site language, the same way WooCommerce creates its Shop page.
archive-{type}The bound page OWNS the archive URL: rename the page and the archive moves with it (the Slug field hides while a page is bound). Without a bound page the slug comes from the Theme Panel. The page itself is never shown directly — it is the editing surface behind the archive.
Good to know
- Group fields outside the loop (repeater, group, flexible content, gallery) are read from the landing page just like single-value fields. The one exception is a group with loop-mode="0" — the project's Logs tab flags it as "archive-scope-unsupported".
- A field placed INSIDE a listing loop belongs to the posts being listed — declare it on the corresponding single-{type} template so it appears on those posts' edit screens.
- If no landing page is configured, nothing breaks: the archive renders exactly as before, and the fields simply stay global-less. You can add the landing page at any time without regenerating the theme.
Forms Extension
Built-in form handling with SMTP, Telegram, validation, and submission storage.
Features
- SMTP email sending (default: smtp.gmail.com:465/SSL)
- reCAPTCHA v3 / v2
- Telegram notifications
- Spam protection without keys — on for new sites
- Auto-reply to form submitter
- JustValidate client-side validation
- CPT
tf_form_entry— submission storage in WordPress - CSV export
Spam protection
Works without keys or outside services. On a new site it is on from the start; on a site that already receives leads, turn it on with "Basic spam protection" under Forms → Settings.
- Filters out submissions that fill a hidden trap field, arrive without the page script, are sent faster than a person can type, carry several links, or skip the image captcha. How often one address can send is limited.
- Filtered submissions are not sent anywhere: they stay in the Spam folder of Submissions for 30 days, and one filtered by mistake can be restored with "Not spam".
- WordPress’s own forms are covered by a separate switch, “Spam protection for WordPress forms”, on by default: comments and product reviews, registration and password reset. A comment a bot sends without opening the page is refused and never stored; a borderline one goes to Comments → Spam; a suspicious registration or reset request is refused with a request to try again. The login form and logged-in users are not checked.
- The image captcha speaks the page language: texts are set per language, and it draws only characters a visitor can type without switching keyboard layout (Ukrainian, Russian, Latin letters or digits).
Against bots built to imitate a person, use reCAPTCHA with Google keys — it is enabled in the same settings.
wp-field — a field key that survives a rename
By default a form field is submitted under the label you typed in Webflow. Rename that label later and the mail template and any delivery mapping that referenced it stop matching — silently, because the submission still arrives. Mark the field (or its wrapper with the label) and the key stops depending on the caption.
<input type="tel" wp-field="phone">
<div class="row" wp-field="phone">
<label for="tel">Phone</label>
<input type="tel" id="tel">
</div>
The key is written into the field when the theme is generated, so nothing needs configuring afterwards. Use Latin letters, digits, _ and -; names reserved by the form pipeline are refused and listed in the project log. Fields whose name is fixed by WordPress or WooCommerce — comments, reviews, checkout — use their own marks instead.
Custom fields engine
The theme stores custom fields through an ACF-compatible engine. Two interchangeable engines share the same fields and the same stored data, so you can switch without losing values.
Two engines, one data format
Secure Custom Fields is the default — a free WordPress.org plugin that auto-updates and provides every ACF PRO field type, with no license. Prefer your own licensed ACF PRO? Install it and the theme detects it automatically.
Multilanguage Options
Built-in multilanguage support for theme options and page fields. Auto-activates with Polylang or WPML.
How It Works
The theme detects Polylang or WPML and loads the appropriate integration automatically.
- Auto-detection: Polylang (pll_current_language) or WPML (ICL_SITEPRESS_VERSION)
- Per-locale storage: each language gets its own set of option values
- Fallback: empty locale fields inherit values from "All (default)"
- Custom flags: upload custom language flags from Media Library
Managing Options by Language
Use the language switcher bar on the Options page to edit values for each language.
| Action | Description |
|---|---|
| "All (default)" | Master values. Used as fallback when a locale has no specific value. |
| Per-language editing | Switch to a language to edit its specific values. Empty fields inherit from "All". |
| Inheritance | Leave a field empty on a locale page to use the "All (default)" value on the frontend. |
Auto-Translate Fields
Built-in machine translation for field values. Available in the sidebar on post edit and options pages.
- Per-field "Translate" button next to each text field label
- "Translate All" button in sidebar — translates all fields at once, including hidden tabs and repeaters
- Link fields auto-skipped (detected by field name and URL value pattern)
- No API key required — uses Google Translate free endpoint
- WYSIWYG (rich text) fields supported — HTML structure preserved
Protected Files
These files are preserved during theme updates and never overwritten.
user-functions.php— Custom PHP functionscss/user.css— Custom CSS stylesjs/user.js— Custom JavaScript
CSS load order: main.css → themeforge.css → user.css. Use user.css to override any theme styles.
Import System
Data and media import from pages.json and menus.json generated during theme creation.
Import Order (Important!)
- Step 1: Upload images (import_images) — uploads all media files to WordPress
- Step 2: Import data (import_data) — imports field values, resolves image filenames to attachment IDs
Images must be uploaded before data import. Reason: image fields are resolved to WordPress attachment IDs at import time.
Import Actions
| Action | Description |
|---|---|
/?action=import_images | Upload all media files from all pages |
/?action=import_data | Import all field data for all pages + theme options |
/?action=import_page_media&slug=X | Upload media for a specific page |
/?action=import_page_data&slug=X | Import field data for a specific page |
/?action=import_menu_data | Import all menus (or single: &menu=slug) |
Page Title Source
When creating WordPress pages during import, the page title is taken from the source HTML in priority order:
<meta property="og:title">— Open Graph<title>— HTML title tag- Template name (e.g. "About" from page-about)
Image Filename Resolution
The import system resolves Webflow image filenames to WordPress attachment IDs using a fallback chain:
- URL-decode filename (handles Cyrillic: %D0%94%D0%B5... → Десерт...)
- Strip 24-hex Webflow hash prefix (695a454c..._ → original name)
- sanitize_file_name() + DB LIKE query for robust matching
Locale-Aware Import
Add &lang=xx to import data for a specific language. The import metabox on page edit screens auto-detects the page language.
/?action=import_page_data&slug=theme-options&lang=en| Context | Description |
|---|---|
| Theme Options | Saves options with a locale suffix (options_LOCALE_field). Use the language switcher on the Options page. |
| Pages | Finds or creates the Polylang translation post, imports data into it. Original page found regardless of current language filter. |
| Import Metabox | On page edit: detects language from post (pll_get_post_language). On options page: reads ?lang= from URL. |
Developer Reference
Technical details for developers and AI assistants. Covers HTML attributes, field types, menu system internals, JavaScript hooks, and helper functions.
Field Types
ACF / SCF field-type mapping. Each attribute type maps to a specific HTML element.
| ACF / SCF Type | Element | Attribute Example |
|---|---|---|
| text | div, span, p, h1-h6 | fld-text="name" |
| textarea | div, p | fld-area="name" |
| wysiwyg | div | fld-editor="name" |
| image | img (src) | fld-image="name" |
| link / url | a (href) | fld-link="name" |
| true_false | wrapper div | fld-show="name" |
| color_picker | any (bg) | fld-bg-color="name" |
| repeater | wrapper div | fld-loop="name" |
| group | wrapper div | fld-group="name" |
| gallery | wrapper div | fld-gallery="name" |
| tab | container div | fld-tab="name" |
Field Groups (wp-fields-group)
The wp-fields-group attribute creates ACF/SCF field groups in WordPress admin.
Usage
<div wp-fields-group="Section Name"> ... fields ... </div>- Wraps fields into a named group (metabox in admin)
- Multiple wp-fields-group groups per page supported
- On theme-options page: all groups merge into one options container
- Without wp-fields-group: fields generate PHP only (no admin UI)
wp-show-editor
Keep WordPress text editor visible on pages with custom fields.
Usage
<body wp-show-editor wp-fields-group="Section">- Place on <body> tag — by default, editor is hidden on page templates with fields
- Use together with wp-fields-group when the page needs both custom fields AND the_content()
- Does not apply to single-post templates (single, single-product) — they always keep the editor
ACF / SCF data compatibility
Secure Custom Fields is a free, drop-in fork of ACF PRO maintained on WordPress.org. It is API-identical and stores values in exactly the same place, so SCF, ACF PRO and any existing ACF data are fully interchangeable.
Same fields, same data
Switching the engine never migrates or rewrites your content — both engines read the same field keys from wp_postmeta / wp_options. Existing ACF data from another theme or a manual setup is picked up by field name, regardless of which engine created it.
Existing & third-party ACF data
If ACF (or SCF) data already exists — from another generator, a previous theme, or a manual field group — the theme reads it as-is by field name. Nothing to import or convert; the values appear in the editor under whichever engine is active.
JavaScript Hooks
User-defined callback functions available in custom.js. The theme checks for these functions and calls them at the appropriate time.
General Hooks
| Hook | When called |
|---|---|
tf_webflow_init() | After AJAX content load (re-init Webflow interactions) |
tf_after_filter() | After AJAX filter results loaded |
WooCommerce Hooks
| Hook | When called |
|---|---|
tf_cart_data_modify(data) | Before add-to-cart (modify cart data) |
tf_after_add_to_cart() | After product added to cart |
tf_after_cart_data_modify() | After cart data modified |
tf_after_variation_change() | After product variation changed |
tf_after_recalc_checkout() | After checkout totals recalculated |
Define these functions in custom.js. Legacy wfp_ prefixed names are also supported for backward compatibility.
Custom Queries
Create WP_Query and term queries through the Theme Panel UI — no code required.
Post Query
Set up in Theme Panel → Custom Queries. Each query has: name, post type, posts per page, pagination, taxonomy filters.
$args = select_query_by_name('my_query');
$query = new WP_Query($args);Term Query
Set up in Theme Panel → Custom Term Queries. Each query has: name, taxonomy, hide_empty, orderby, order.
$args = select_term_query_by_name('my_terms');
$terms = get_terms($args);Helper Functions
PHP functions available in templates and user-functions.php.
General
| Function | Returns |
|---|---|
tf_default_thumbnail() | Default placeholder image URL (configurable in Theme Settings) |
tf_declense($n, $f1, $f2, $f5) | Slavic plural form: pick correct word form for a number (1 товар / 2 товара / 5 товаров) |
tf_num_decline($n, $titles) | Plural with number prefix: "5 товаров". Accepts array or comma-string. |
search_context($chars) | Echo text excerpt around the search query match |
device_is($type) | Detect device: phone, tablet, mobile, desktop, ios, android |
WooCommerce
| Function | Returns |
|---|---|
is_sale() | Whether current product is on sale |
get_price($type, $format) | Product price: "price", "price_sale", "price_regular". Format: "default", "extreme", "range" |
get_cart_info($info) | Cart totals: total_without_discount, total_with_discount, discount_amount, discount_percentage, coupon_discounts |
get_cart_volume() | Total cart volume in cubic meters |