Tuli Builder

内置于Tuli Builder的WordPress MCP服务器:AI代理可构建页面、模板、查询循环和自定义文章类型。

文档

Requirements

  • WordPress 6.0 or newer
  • PHP 7.4 or newer
  • An active Tuli Builder licence (one-time purchase, see pricing)
  • To build with AI: nothing else. Tuli AI is included with your licence and runs on prepaid AI credits. If you would rather use your own account, an API key from OpenAI, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Z.AI (GLM), OpenRouter or any OpenAI-compatible service works too, as does any MCP-compatible AI tool such as Claude Code, Codex CLI, Gemini CLI, Cursor, Windsurf, VS Code, OpenCode, Antigravity or Claude Desktop

Local and staging copies of a site do not count against your licence's site limit.

Install and activate Tuli Builder

  1. Buy a licence on the pricing page. You get your licence key by email and in the Customer Portal.
  2. In the Customer Portal, open Download and download the Tuli theme zip.
  3. In WordPress go to Appearance → Themes → Add New → Upload Theme, choose the zip, then click Install Now and Activate.
  4. Go to Tuli → Settings → License, paste your licence key and click Activate.

Tuli is a theme and a page builder in one, so you don't need a separate builder plugin. The AI features (assistant and MCP server) need an active licence. The visual builder keeps working even if a licence expires.

AI Assistant

The built-in AI Assistant is the quickest way to build with AI. Describe what you want, for example "Build me a portfolio website for a photographer", and watch it appear. It is available in two places with identical capabilities: in the WordPress admin dashboard at Tuli → AI Assistant (recommended) and inside the visual builder when you edit a page. Chats you start are saved and show up in both places, and the assistant can keep working in the background after you close the browser.

Tuli AI: the easiest way to start

Tuli AI is the assistant's default provider and is included with your licence. There are no API keys to find, no provider accounts to create and no billing to set up on your own. Once your licence is active, Tuli AI is selected for you and you can start building straight away.

  1. Activate your licence key (see above). Tuli AI becomes available immediately.
  2. Open Tuli → AI Assistant and describe what you want to build.
  3. Top up credits whenever you run low, from AI Credits in the Customer Portal.

How AI credits work

  • Tuli AI runs on a simple prepaid credit balance. One credit is one US cent of AI usage, so a $10 top-up adds $10.00 of credit.
  • Credits never expire. Top up from $5 to $100 in the Customer Portal and the credits are added as soon as the payment goes through, using a saved payment method if you have one.
  • Your current balance is shown next to Tuli AI in Tuli → Settings → AI Assistant, with a link to top up.
  • Tuli AI needs an active licence on the site.

Or use your own AI provider

Prefer your own account? Add an API key for any provider below and pick it in the same settings screen. You then pay that provider directly for what you use, and Tuli credits are not used.

  1. Go to Tuli → Settings → AI Assistant and pick a provider.
  2. Paste that provider's API key (each provider panel has a Get a key link) and choose a model.
  3. Click Save settings. A green dot appears on every provider that is set up.

Supported providers

ProviderWhere to get a keyGood for
Anthropic (Claude)platform.claude.comTop design and code quality
OpenAIplatform.openai.comGPT models, strong all-rounder
Google Geminiaistudio.google.comFast, strong coding, low cost
xAI (Grok)console.x.aiGrok models
DeepSeekplatform.deepseek.comVery cheap, text-only
Z.AI (GLM)z.aiLow-cost GLM models, text-only
OpenRouteropenrouter.ai/keysOne key for hundreds of models from every major lab
Custom (OpenAI-compatible)Your own endpointOllama, LM Studio, Perplexity, Fireworks, Cerebras or a company gateway

Tuli AI needs no key. Each other provider lists its current models in the dropdown, newest and most capable first. Models that accept images can use screenshots as design references; text-only models (such as DeepSeek and GLM) cannot. Not sure which to pick? Anthropic (Claude) gives the best design results, and Gemini is a cheap, fast option.

Using a custom or local model

Choose Custom (OpenAI-compatible), enter a name, the Base URL (the API root ending in /v1, or the full /chat/completions URL) and the exact model ID, for example llama3.3:70b for a local Ollama server. The API key is optional for local servers.

Using the assistant

  • In wp-admin (recommended): open Tuli → AI Assistant in the WordPress dashboard for a full-page chat that works across the whole site, without opening a page first. Describe what you want, for example "Build me a portfolio website for a photographer." Type @ to attach a page or template as context.
  • In the builder: the same assistant, with identical capabilities and the same site tools, is also available in the AI Assistant panel when you open a page with Edit with Tuli. Chats you start are saved and show up in both places.
  • The assistant writes HTML, CSS and JavaScript live on the canvas, and it checks its own work: where possible it measures the finished page (spacing, overflow on phones) with a built-in headless browser, and otherwise runs a static layout review, then fixes what it finds. Review the preview, then click Save to keep the changes.

Site tools

Wherever you open it, in wp-admin or in the builder, the assistant can design pages and work on the whole site with the same tools an MCP agent gets. In Tuli → Settings → AI Assistant → Site tools you choose what it may do:

  • Read the site and content
  • Create, edit and delete posts and pages
  • Tuli building: page HTML/CSS/JS, header and footer, snippets
  • Templates and query loops
  • Taxonomies and terms
  • Media uploads
  • Plugins: install, activate, deactivate
  • Custom post types, taxonomies and ACF fields
  • Code execution: plugins, files and WP-CLI

Every action still requires the user's own WordPress permission. For example, an Editor can't install plugins even if the box is ticked. Your API keys are stored on your server and are never sent to the browser.

MCP Server

Tuli Builder includes a WordPress MCP server (Model Context Protocol, Streamable HTTP). Once an AI agent is connected, it can manage the site for you: pages, posts, Tuli layouts, templates, query loops, forms, custom post types, taxonomies, media, plugins and global CSS/JS.

Already pay for Claude Code, Codex or another AI tool? Use MCP.

Most AI subscription plans (Claude Code, ChatGPT/Codex and similar tools) don't allow third-party software to connect to them or spend your subscription, and they do not give you an API key, so you can't put a subscription login into the AI Assistant settings. The MCP server is the right route: your agent keeps running inside the tool you already pay for (Claude Code, Codex CLI, or whichever AI tool or subscription you use) and simply connects out to your site to do the work. Use the built-in AI Assistant (Tuli AI or your own API key) when you want to build without leaving WordPress.

Which should I choose?

You have…Use
Nothing yet and you want to start in one minute (Tuli AI, included with your licence), or an API key from any providerThe built-in AI Assistant
An AI subscription with no API key (Claude Code, Codex or any other)The MCP server (this section)
Cursor, Windsurf, VS Code, Gemini CLI, OpenCode or AntigravityThe MCP server (this section)

Connect in four steps

  1. Go to Tuli → Settings → MCP Server and tick Enable the MCP server for this site. Save.
  2. Under Application passwords, create a password. Name it, choose which user it acts as, tick the actions it's allowed to perform (or choose full access), and add optional instructions for the agent.
  3. Copy the ready-made prompt Tuli shows you. The password is shown only once, so copy it straight away.
  4. Paste that prompt into your AI tool chat (Claude Code, Codex, Cursor, Gemini CLI, Antigravity and others). The agent adds the server by itself and starts working. The same screen also has copy-paste snippets for every client if you would rather add it by hand.

You can edit the permissions of, or revoke, any application password at any time. Agents can never change or switch your theme. The MCP server requires an active licence.

Manual setup: connection details and commands for each client

Connection details

  • Endpoint: https://your-site.com/wp-json/tuli/v1/mcp
  • Transport: HTTP (Streamable HTTP)
  • Header: Authorization: Bearer YOUR_APPLICATION_PASSWORD

Claude Code

claude mcp add --transport http my-site https://your-site.com/wp-json/tuli/v1/mcp --header "Authorization: Bearer YOUR_APPLICATION_PASSWORD"

OpenAI Codex CLI

codex mcp add my-site -- npx -y mcp-remote https://your-site.com/wp-json/tuli/v1/mcp --header "Authorization: Bearer YOUR_APPLICATION_PASSWORD"

Gemini CLI

gemini mcp add --transport http my-site https://your-site.com/wp-json/tuli/v1/mcp --header "Authorization: Bearer YOUR_APPLICATION_PASSWORD"

Cursor, Windsurf, VS Code and other JSON-configured clients

{
  "mcpServers": {
    "my-site": {
      "type": "http",
      "url": "https://your-site.com/wp-json/tuli/v1/mcp",
      "headers": { "Authorization": "Bearer YOUR_APPLICATION_PASSWORD" }
    }
  }
}

Where the file goes: Cursor uses ~/.cursor/mcp.json or .cursor/mcp.json. Windsurf uses ~/.codeium/windsurf/mcp_config.json (with serverUrl instead of url). VS Code uses .vscode/mcp.json. OpenCode uses opencode.json. The Tuli settings screen shows the exact snippet for each client.

Claude Desktop and other stdio-only clients

{
  "mcpServers": {
    "my-site": {
      "command": "npx",
      "args": ["mcp-remote", "https://your-site.com/wp-json/tuli/v1/mcp", "--header", "Authorization: Bearer YOUR_APPLICATION_PASSWORD"]
    }
  }
}

This uses the mcp-remote bridge, which requires Node.js. Restart the client after saving.

What to ask the agent

  • "Create a landing page for our spring sale with a hero, three feature cards and a signup CTA."
  • "Add a Property custom post type with price, beds, baths and city fields, six sample listings and a filterable listings page."
  • "Write and publish a blog post about our new pricing, with a featured image."
  • "Build a header and footer template and show them on every page."

Video walkthrough: Build a WordPress website using Claude Code through the MCP server.

How do I connect Claude Code to WordPress?

Enable the MCP server under Tuli, Settings, MCP Server, create an application password, and paste the prompt Tuli gives you into Claude Code. Claude Code then adds the server and can build and edit pages on your site.

Can I use my Claude Code or Codex subscription to build a WordPress site with Tuli?

Yes. Connect it through the MCP server. The agent keeps running inside your subscription tool and connects out to your site, so no API key is needed.

Which AI tools work with the Tuli MCP server?

Any MCP-compatible client works, including Claude Code, Claude Desktop, OpenAI Codex CLI, Gemini CLI, Cursor, Windsurf, VS Code, Cline, OpenCode and Antigravity.

Is the Tuli MCP server safe to use?

It is off by default. Each application password is tied to one user, limited to the actions you tick, and can be revoked at any time. Agents cannot change or switch your theme.

Visual builder

Tuli's visual builder is a way of editing ordinary HTML and CSS without having to touch the code. Whatever you do in it, whether you drag an element, move a slider, or ask the AI to change something, ends up as plain HTML, CSS and JavaScript. There is no proprietary page format underneath. That is why you can switch between the visual panels and the raw code at any time.

Everything in Tuli is HTML

A Tuli page is real HTML. Real tags such as <section>, <div>, <h1>, <p>, <a>, <img>, <ul> and <form> become page builder elements you can click, drag, reorder and style visually. Edit the HTML and the elements change. Edit the elements and the HTML changes. The same idea carries through the rest of Tuli: forms, query loops, templates and dynamic data are all written in plain HTML too.

Open the builder

Open any page under Pages and click Edit with Tuli. The button appears on the edit screen, in the page list row actions and in the admin bar. By default the builder is on for Pages and Templates; turn it on for other post types in Tuli → Settings → General. The canvas in the middle is your real page, the left panel holds the element library and the Structure tree, and the right panel holds the Content and Style tabs.

Every HTML tag becomes an element

Tuli reads the page's HTML and turns every tag into an element you can select. The Structure panel shows them as a tree, exactly mirroring the HTML nesting: a section contains a container, the container contains a heading and a paragraph, and so on.

  • Real tags, real attributes: an element is simply a tag with its attributes (class, id, href, src, alt, data-* and so on). The Content tab edits text, links, image sources and similar attributes, and the Style tab edits how the element looks. The HTML you would write by hand and the element you see in the tree are the same thing.
  • The library is ready-made HTML: drag sections, containers, headings, text, buttons, images, sliders, tabs, accordions, forms, menus, query loops and more onto the canvas or into the tree. Each library element is just a small piece of HTML that gets inserted.
  • Paste any HTML: paste a full design (from a developer, a template, ChatGPT, Claude or Lovable) and every tag appears as an editable element, with its CSS and JavaScript preserved. See Convert HTML to WordPress.
  • Special elements are still HTML: features that need the server, such as forms, query loops, templates and dynamic data, are written as normal tags with a few data- attributes. Tuli fills them in with live content when the page is rendered.
  • Stable internal ids: Tuli stamps a stable id on each element behind the scenes, so styles keep pointing to the right element as you reorder, duplicate or copy and paste it. Duplicating an element copies its styles too.
  • Undo and redo: every change is tracked, so you can step back freely.

Where your code lives

A Tuli page is made of five simple pieces, plus the theme settings that every page shares:

WhatScopeWhere it is stored and served
HTMLOne pageThe page's content field, so it is revisioned by WordPress like any other content
Page CSSThat page onlyA generated file, tuli-{page id}.css
Page JSThat page onlyA generated file, tuli-{page id}.js
Global CSSWhole siteA generated file, tuli-global.css
Global JSWhole siteA generated file, tuli-global.js
Theme settingsWhole sitetuli-theme.css: CSS variables (colours, fonts, design tokens) plus baseline body, heading and container styles

The generated files are written to wp-content/uploads/tuli/. If that folder is not writable, Tuli serves identical CSS and JS from a virtual URL instead, so nothing breaks. Turn on Minify CSS & JS in Tuli → Settings → General to serve minified versions.

How styling works: classes, ids and breakpoints

  • Class styles are site-wide. When you give an element a class (for example btn) and style that class, the rule goes into Global CSS and applies everywhere the class is used. Edit it once and every button on the site changes.
  • Id styles are per page. When you style one specific element, the rule is written against its id and goes into that page's Page CSS, so it never bloats the shared Global file.
  • Tablet and mobile are real media queries. The device toggles write @media (max-width: 991px) for tablet and @media (max-width: 599px) for mobile, around the same selector. Desktop is the base rule.
  • No inline styles. Tuli keeps styles in CSS instead of style="" attributes, so everything you set is visible and editable in the CSS boxes.
  • Clearing a value removes it. Click the green dot next to any property (or a whole section) to clear it, and the property is deleted from the CSS file itself, not just hidden.

How the front end renders it

Visitors get a normal fast WordPress page. Tuli prints your saved HTML as the page body and loads the generated stylesheets in a fixed order, so later files win:

  1. Theme CSS: your design tokens and baseline styles.
  2. Plugin CSS: anything other plugins enqueue.
  3. Global CSS: everything the builder produced for classes and elements.
  4. Page CSS: that page's own overrides, last, so a page can always override the site-wide look.

Global JS and Page JS are loaded the same way. Server-side features run when the page is built: PHP blocks and shortcodes are rendered by WordPress, dynamic data tokens are replaced with real values, query loops repeat once per result, forms are filled in with their fields and security tokens, and templates such as headers and footers are placed wherever their display conditions say. What the visitor receives is plain HTML and CSS.

Two ways to work, and they stay in sync

Visual

Use the Style tab: spacing boxes, colour pickers, typography, backgrounds, layout, borders and effects. Every change updates the canvas immediately and writes the right CSS rule for you.

Code

Open the CSS modal to edit the Global CSS and Page CSS boxes directly, or the JS modal for Global JS and Page JS. Each selected element also has a CSS rule editor at the bottom of the Style tab.

The Style tab, the CSS boxes and the canvas are all views of the same data, so they never disagree. Type padding-top: 5rem; in the Page CSS box and the matching field in the Style tab shows 5rem straight away. Change the field instead and the CSS box updates. You can mix the two freely: let AI or the style panel do the bulk of the work, then fine-tune in code, or the other way round. Nothing is written to the server until you click Save, so you can experiment safely.

What the AI uses

The AI assistant and any MCP agent work on exactly these same pieces: they write the page's HTML, put reusable class rules into Global CSS and one-off rules into Page CSS, and add JavaScript to Global or Page JS. That is why anything they build opens in the visual builder as normal editable elements, and anything you build by hand can be refined by the AI afterwards.

Good to know

  • The native editor is locked on Tuli pages. Editing a Tuli page in Gutenberg or the Classic editor would strip the internal ids and rewrite the markup, so always use Edit with Tuli.
  • No lock-in. The output is standard HTML and CSS files, and you can download the whole site as static files with Export as static HTML.
  • Raw code is safe by default. Tuli sanitises HTML, CSS and JS on save (for example it strips on* event attributes and javascript: URLs). You can switch off PHP execution in the builder from the General settings tab.

Convert HTML to WordPress

Already have a design? Paste HTML, CSS and JavaScript into the builder, and Tuli maps every tag into the Structure panel as an editable element. This works for hand-coded pages, templates you bought, and sites generated with ChatGPT, Claude, Lovable or other AI tools. Selectors, shorthands, comments and media queries are kept as they are. Pasted class rules go into Global CSS so you can reuse them on other pages, and pasted scripts go into Global JS.

Step-by-step guides: How to convert HTML to WordPress and From ChatGPT to WordPress.

Templates: header, footer, single, archive

Under Tuli → Templates you can build headers, footers, single-post layouts, archives, 404 and search pages, plus reusable sections. Each template has display conditions: include and exclude rules with a priority, so you control exactly where it appears. Build a section once and reuse it on any page with the Template element.

Query loops

A query loop repeats one element once for every result of a query: a grid of blog posts, a list of products, the rows of an ACF repeater, a team page. Like everything in Tuli it is plain HTML. You design one card, switch the loop on, and Tuli prints a copy of that card for each result, using each result's own data.

How it works under the hood

A loop is just an attribute on a normal tag: data-tuli-query="q_abc123". The id points to a saved query (its source and settings). When the page renders, Tuli finds that element, runs the query, and replaces the element with one copy per result. If the query returns nothing, the element is removed from the page. If the saved query was deleted, the element is hidden on the live site.

Turn a loop on

  1. Build the card you want repeated (for example a <article> with an image, a title and a button). Put the dynamic values inside it with dynamic data tokens such as {get_the_title}.
  2. Select the outer element of the card, open the Content tab and switch on the Query loop section at the top.
  3. Pick a source, give the query a friendly Query name (for example "Latest posts") and set its options. Changes appear on the canvas straight away.

In the editor the original card stays editable and the other results are shown as read-only preview copies. Edit the card once and every copy follows. Style the card normally: Tuli keeps the styles attached to every copy, even though each copy gets its own element ids.

Query sources

SourceWhat it loopsBest for
Main QueryThe loop WordPress already built for the current URL: a category's posts, search results, the blog index, a shop's products. On a single page it is that one post.Archive, search and shop templates. Needs no setup and always matches the URL.
WP_QueryA custom query you configure with fields (see below)."Latest 3 posts", "Featured products", "Posts in category X" on any page.
PHPA snippet of PHP that returns a WP_Query arguments array.Anything the fields can't express.
ACF RepeaterThe rows of an Advanced Custom Fields repeater. Enabled only when ACF is installed.Pricing tiers, FAQ rows, team members, galleries.
Related ProductsWooCommerce related products (shared categories and tags) of the current product.Single Product templates.
Up-sellsThe up-sell products linked on the current product.Single Product templates.
Cross-sellsCart based cross-sells, calculated the same way WooCommerce does (products linked to what is in the cart, minus items already in it).The Cart page.

The three WooCommerce sources are listed under "Others" in the source picker. Each has a single option, Number of products (1 to 24, default 4). In the editor they show recent products so the card is never empty while you design; on the live site visitors see only the genuine related, up-sell or cross-sell products.

WP_Query options

  • Post type: posts, pages, products or any custom post type.
  • Posts per page: from 1 to 100, or -1 for all.
  • Order by and Order: date, title, menu order, random, ID, modified date, comment count or name, ascending or descending.
  • Status: published (default), draft, pending, private, scheduled or any.
  • Include IDs and Exclude IDs: comma separated post ids to force in or keep out.
  • Offset: skip the first N results (for example to show a featured post separately, then the rest).
  • Taxonomy rows: pick a taxonomy, match by term id, slug or name, and choose IN, NOT IN, AND, EXISTS or NOT EXISTS. With two or more rows, choose whether they combine with AND (match all) or OR (match any).
  • Meta rows: a custom field key, a value and a comparison (=, !=, >, >=, <, <=, LIKE, NOT LIKE, IN, NOT IN, EXISTS, NOT EXISTS), also combinable with AND or OR. This is how you filter by ACF values such as "featured = 1".

Main Query details

A Main Query loop has one optional setting, Posts per page. Leave it at 0 to inherit the WordPress setting (Reading → "Blog pages show at most", or WooCommerce's products per page on a shop). Set a number to override it, or -1 to show everything. The override is applied to the real query before it runs, so pagination and result counts stay correct. It applies to archive, search and blog pages that a Tuli template controls.

Because the Main Query shows "whatever this URL is", build your category, tag, author, search and shop layouts once as a template and every URL of that kind fills itself in. In the editor the canvas previews recent content of the post type the template targets, with the same number of rows the live page will show.

PHP queries

Choose PHP and write a snippet that returns an arguments array. The editor checks the syntax as you type without running anything.

return [
  'post_type'      => 'post',
  'posts_per_page' => 6,
  'meta_key'       => 'views',
  'orderby'        => 'meta_value_num',
  'order'          => 'DESC',
];

Security: only administrators who also have the unfiltered_html capability can save or run a PHP query. The snippet is signed when it is saved, so a snippet written straight into the database never runs. A syntax error or exception simply renders zero results instead of breaking the page, and the site-wide PHP kill switch in Tuli → Settings → Security turns PHP queries off entirely.

ACF Repeater loops

Enter the repeater's Field name and choose where the rows come from: the Current post, a Specific ID or an Options page. Inside the card, print each row's sub-fields with {the_sub_field:field_name}. If ACF is not active the loop renders nothing.

What you can use inside a loop

Every dynamic data token resolves for the current result, so the same card shows a different title, link and image each time.

<article class="card">
  <a href="{the_permalink}">
    <img src="{get_the_post_thumbnail_url:null,large}" alt="{the_title}">
  </a>
  <h3>{the_title}</h3>
  <p>{tuli_post_excerpt:120,…}</p>
  <p>{get_the_date} · {get_the_author}</p>
</article>

WooCommerce element parts (Product Title, Price, Add to Cart and so on) also work inside a product loop and each card shows its own product. You can also use visibility conditions on elements inside the card (see Dynamic data).

Pagination

Add the Pagination element below a loop to get page links. It is a small marker, <div data-tuli-pagination="1">, that Tuli fills with real links.

  • It binds to the first paginatable loop on the page. To target a specific loop, choose it in the pager's Content tab settings.
  • Main Query loops use WordPress's own /page/2/ addresses. Custom WP_Query loops page with a ?tuli_paged=2 address so they never clash with WordPress's own paging.
  • You can change the Previous and Next text and how many numbers show around the current page, and set button, border, radius and active-page colours in the Content tab. Style everything else through the .tuli-pagination-nav class.
  • With only one page of results nothing is printed. In the editor a sample pager is shown so you can design it.
  • The pager is universal: it works for any post type, not only shop products.

Only loops that a pager points at count the total number of results, so plain loops stay fast.

Filter element: live filtering

The Filter element lets visitors narrow a loop without a page reload. Drop one or more on the page and point each at a loop (leave the target empty to use the first loop).

Filter typeWhat the visitor sees
TaxonomyTerms of a taxonomy (categories, tags, product categories, any custom taxonomy) as a dropdown, checkboxes or radio buttons.
FieldA custom field (ACF or any meta key) as a dropdown, checkboxes, radio buttons, a text box with a chosen comparison (=, !=, LIKE, >, < and so on) or a min/max range slider with optional prefix and suffix (for example a price). Empty slider bounds are detected from your stored data.
SearchA search box that searches the loop's content.
SortA dropdown: Newest, Oldest, Title A-Z, Title Z-A, Price low to high, Price high to low, Random.
PaginationA pager that works with the filters and updates its page count as the results narrow.
  • It works on both loop kinds. Custom WP_Query loops get the filter constraints added to their query. Main Query loops (archive and shop pages) are filtered through WordPress's own query, so shop and archive pages can be filtered too.
  • Shareable and SEO friendly. The active filters live in the address bar (?tf_category=news, ?tf_q=shoes, ?tf_sort=price_asc). A filtered link can be shared or bookmarked and renders the filtered results on the server when opened. Several values in one filter (checkboxes) are comma separated.
  • No admin-ajax. Changing a control re-requests the same page with a partial flag and swaps in only the loop and the pager, so WordPress and WooCommerce set up the query exactly as for a normal visit.
  • Filters combine with each other and with the loop's own rules using AND.
  • Each control can be given a label, an "All" option text, placeholder text, a horizontal or vertical layout, and colours (text, background, border, radius, accent) in the Content tab.

If you use the WP Grid Builder plugin, Tuli also offers Facet and Grid elements and lets WP Grid Builder facets filter Tuli's Main Query and WP_Query loops. This is optional and only appears when that plugin is active.

Ask the AI to build loops

With the AI Assistant or MCP you can describe the result and let the AI create the saved query and wire the element for you (it uses the save_query and list_queries tools):

Create a "Latest articles" section on the home page: a 3 column grid of the 6 newest posts from the category "news", each card with featured image, title, a 120 character excerpt and a read more link. Add a Pagination element below it.
Build an Archive template for the "product" post type with a Main Query loop of 12 products per page, a Filter for product categories, a price range filter, a sort dropdown and pagination.

Good to know

  • Use classes for styling, not inline styles, so the look applies to every copy.
  • Keep the loop on the outermost element of one card. Everything inside it is repeated.
  • Repeater rows, tags and categories inside a card are best printed with tokens ({the_tags}) or a second loop of the right source.
  • If a card shows blank values in a single or archive template, set a preview post in the template so the editor has real content to show.

Dynamic data

Dynamic data prints live values into your HTML: the post title, a permalink, the featured image, an ACF field, the site name, a custom function's result. You write a function name in curly braces, {get_the_title}, anywhere in your text or in an attribute, and Tuli replaces it with the real value when the page is shown. There is no PHP to write and no shortcode to register.

Syntax

{function_name}
{function_name:argument}
{function_name:argument1,argument2}
  • Use it in text (<h1>{get_the_title}</h1>) and in attributes (<a href="{get_permalink}">, <img src="{get_the_post_thumbnail_url}">, alt="{the_title}").
  • Arguments follow a colon and are separated by commas. Numbers become numbers, true and false become booleans, and null (or an empty slot) skips an argument. Quotes are not supported, so an argument can't contain a comma.
  • The function may echo its output or return it. Tuli prints either, so you never need to know which kind a function is.
  • The output is inserted as-is, so a function can return HTML such as links or an image tag.
  • Write the braces as plain { and }. If a tool or editor has turned them into %7B and %7D inside a link, Tuli repairs the known tokens automatically and saves them back in the clean form.

Insert a token from the builder

Click the database icon on any text or attribute field to open the dynamic data picker. It is grouped, searchable by name or description, and inserts the token at your cursor. In the canvas, tokens are shown as their real values so you see what visitors will see, while the original {token} is what is saved.

Built-in tokens

GroupTokens
Post{get_the_title} title · {get_permalink} URL · {the_content} full content · {get_the_excerpt} excerpt · {tuli_post_excerpt:120,…} excerpt cut to a character count with your own "read more" text · {get_the_date} · {get_the_modified_date} · {get_the_ID} · {get_post_field:post_name} slug · {get_post_type} · {get_the_post_thumbnail_url} featured image (pass a size: {get_the_post_thumbnail_url:null,large}) · {get_comments_number}
Taxonomy{the_category} category links · {the_tags} tag links · {single_term_title} the current archive term
Author{get_the_author} name · {get_the_author_meta:description} bio
Site{get_bloginfo:name} title · {get_bloginfo:description} tagline · {home_url} · {admin_url} · {get_template_directory_uri} theme URL · {wp_login_url} · {wp_logout_url}
Current user{get_current_user_id}
Loop{the_title} · {the_content} · {the_permalink} for the current loop result, and {the_sub_field:field_name} inside an ACF repeater loop
ACF fieldsWhen Advanced Custom Fields is active, every top-level field appears in the picker as {the_field:field_name}. Fields inside a repeater use {the_sub_field:name} within a repeater loop.

Which data a token reads depends on where it sits. On a normal page it reads that page. In a single-post template it reads the post being viewed. In a query loop it reads the current loop result. In a header or footer template it reads the page the visitor is on. In the editor, set a preview post on single and archive templates so the canvas fills with real values.

Print your own custom functions

The same braces work for functions you write yourself. If you need to display something custom on the front end, such as an opening-hours status, a formatted price, a calculated value or the result of an API call, write a small PHP function that returns or echoes the value, then print it with {your_function_name} anywhere in your HTML.

function my_shop_open_status() {
    return ( (int) wp_date( "G" ) >= 9 && (int) wp_date( "G" ) < 17 ) ? "Open now" : "Closed";
}
add_filter( "tuli/tokens/allowed_functions", function ( $functions ) {
    $functions[] = "my_shop_open_status";
    return $functions;
} );

Put that PHP in a Code Snippet, then use the function in your page:

<p>We are {my_shop_open_status} today.</p>
  • Pass arguments after a colon, like {my_price_label:42,USD}.
  • Inside a query loop the function runs once per result, so it can use get_the_ID() or get_post_meta() for the current item.
  • The tuli/tokens/allowed_functions line is a safety allowlist. Only the built-in tokens above and functions you register can run, so stray curly braces in your content (in a tutorial, a code sample or a JavaScript object) are never executed as code. Anything not on the list passes through untouched.
  • You don't have to write any of this yourself. Ask the AI: "Create a function that shows whether we are open and register it as a dynamic data token."

Show a token as plain text

To document tokens on a page without running them, put them inside <code>, <pre>, <kbd> or <samp>. Anything inside those tags is shown literally. For other markup add the attribute data-tuli-no-tokens to the element and everything inside it is left alone. This page uses code blocks for exactly that reason.

Show or hide elements with visibility conditions

Select an element and open Visibility conditions in the Content tab to show it only when rules match. Hidden elements are removed from the HTML on the server, not just hidden with CSS, so they never reach the visitor. Add several rules and join each with AND or OR; they are read from top to bottom.

Rule typeExamples
UserLogged in, logged out, has or lacks a role, has or lacks a capability, is an administrator.
Page / contextFront page, blog page, a single post of a type, a post type archive, any archive, category, tag, a taxonomy, search results, 404.
Date / timeAfter or before a date and time, or on certain days of the week. Great for launches, sales and "closed on Sundays" notes.
URL parameterA query-string value is equal, not equal, contains, doesn't contain, is empty or is not empty (for example ?utm_source=newsletter).
Dynamic dataCompare any token to a value: equals, not equals, contains, doesn't contain, is empty, is not empty, greater or less than. Both sides can be tokens, so {get_the_ID} can be compared with another dynamic value.

If a condition can't be evaluated, the element is shown, so a typo never silently hides content. Conditions only run if an administrator saved them; they carry a signature that is checked on every request. Running raw PHP as a condition is no longer supported, so use the rules above or a custom token function instead.

Examples

<a class="btn" href="{get_permalink}">Read {get_the_title}</a>

<img src="{get_the_post_thumbnail_url:null,large}" alt="{get_the_title}">

<footer>© {get_bloginfo:name} · <a href="{wp_login_url}">Log in</a></footer>

<p>Price: {the_field:price}</p>

Ask the AI to do it for you, for example: "On the Single Post template, show the featured image, title, author and date from dynamic data, and a related posts loop under the content."

Security notes

  • Only allowlisted functions run, with the arguments you typed. Visitors can't change what is called.
  • A failing function returns an empty string instead of breaking the page.
  • Token output is not escaped on purpose, because a token such as {the_content} legitimately returns HTML. When you write your own function, escape any user supplied data with esc_html() or esc_attr() before returning it.

Forms

Tuli forms are HTML. You write (or paste, or have the AI write) an ordinary HTML form, and Tuli turns it into a fully working form: it handles the submission, validates every field on the server, sends the emails, runs the actions you choose, stores the entries and blocks spam. You need no form plugin and no PHP.

Build a form your way

Go to Tuli → Forms → Add new form. Each form has four tabs: Build, Styles, Actions and Settings. In the Build tab you choose how to make it:

  • HTML mode: paste a whole <form>, or just its fields, from anywhere: a design you already have, a form generated by ChatGPT or Claude, or hand-written markup. Plain inputs, textareas, selects, checkboxes, radios and file uploads all work. Scripts are removed, and everything else is kept as you wrote it.
  • Visual mode: add fields from a list, drag them into place and set labels, widths, placeholders and rules without writing code. Tuli writes the HTML for you, and you can switch to HTML mode at any time to see or edit it.

A plain <form> with fields that you paste into a page is picked up as a managed form too, so a pasted design does not lose its form. The form's own look is under the Styles tab (colours, shape and your own CSS), and Preview opens the saved form in a new tab. Real submissions from a preview follow the form's settings.

Place it on a page

  • Drag the Form element onto a page and choose the form, or
  • Use the shortcode shown in the Tuli → Forms list, for example . It works anywhere shortcodes work: Tuli pages, classic posts, widgets and theme templates.

The list also shows where each form is used and how many entries it has, and lets you duplicate, trash and restore forms. Visitors submit without a page reload when JavaScript is available, and the form still works as a normal submission when it is not.

Server-side validation

Tuli keeps your original form markup on the server and checks every submission against it, not against whatever the visitor's browser sent. Deleting required in the browser's developer tools does not get an empty field through. The server enforces:

  • Required fields, including required checkboxes, radio groups, selects and file uploads
  • Field types: email addresses, URLs, numbers, dates and times, and colours
  • Limits: minlength, maxlength, min, max, step and pattern
  • Allowed options: a select, radio or checkbox value must be one of the options that exist in the form
  • Tampering: fields that are not in the form, disabled fields, and changed hidden or read-only values are rejected, and the attempt is logged as spam
  • Uploads: only the allowed file types and sizes are accepted, scripts and executables are never accepted, and the file's real content must match its extension
  • Security token: every submission carries a one-time WordPress nonce

When a rule fails the visitor sees a plain message such as "Please fill in Email" or "Message must be at least 20 characters", so they can fix it and resubmit.

Form actions

The Actions tab decides what a form does with a submission. A form has an ordered list of actions, and you can add the same type more than once, for example email the office and the shop, or create a post and also register the person who sent it. Each action maps your form's own fields onto what it creates, so you never write code. Actions run after the spam checks have passed and after the entry has been saved, so a failing action never costs you the submission.

ActionWhat it does
Send an emailEmails the submission with the answers in the body and any uploaded files attached. Set the recipients (a comma-separated list; blank sends to the site admin email), the subject, and which form field becomes the Reply-To address so you can answer the sender directly. The sender's IP address is added to the email unless you turn IP recording off.
Create a postFiles each submission as a post of any post type, including custom ones. Use it for listings, testimonials, job applications or support tickets. Choose the status (draft, pending review, private or published), map the title, content and excerpt to form fields (or fill the content with all submitted fields), set an author, and save answers as custom fields, including ACF fields.
Create a userRegisters the person who submitted the form as a WordPress user, for member sign-ups and applications. Choose the role (pick a safe one such as Subscriber), map the email, username, first and last name and password fields, decide whether to send the new-user notification, and save extra answers as user meta.

You can also set the message visitors see after a successful submission for each form, or use the site-wide wording.

Entries

Every submission is stored under Tuli → Forms → Entries alongside the context that helps you judge it: the field values, the page it came from, the referrer, the visitor's IP address and browser, the logged-in user if there was one, and (optionally) the country.

  • Filter by status (unread, spam and so on) and by form, open any entry, mark it as spam or not spam, or delete it.
  • Export CSV: download entries filtered by form, status and date range. Columns line up even when you export several forms at once.
  • Retention: delete entries older than a number of days automatically (0 keeps them forever).
  • Privacy: turn off storing entries for a single form, or turn off IP recording and country lookups if your privacy policy requires it.

Spam protection

Spam protection has one master switch in the Settings tab, with these layers behind it. Blocked attempts are filed under Entries → Spam with a "Blocked because" reason, so you can see what each rule caught and correct a false positive.

ProtectionWhat it does
HoneypotA hidden field that people never see. A bot that fills it is rejected.
Minimum fill timeRejects forms sent faster than a person could fill them (3 seconds by default, adjustable). It applies when JavaScript is available.
Submissions per IPCaps how many times one address can submit, for example once every 24 hours, so a form cannot be used to flood your inbox. Logged-in administrators are never limited, so you can keep testing.
Blocked IP addressesOne site-wide list, managed under Tuli → Forms → Blocked, that turns those visitors away on every form. Use * to block a range, such as 203.0.113.*. You can also block an address straight from an entry.
Blocked keywordsOne per line, case-insensitive. A submission containing any of them in a field value is rejected.
Link limitLimits how many links a message may contain. Setting it to 0 rejects any message with a link, which is the most effective filter against link-spam bots. Email addresses are not counted as links.
Alphabet allowlistAccept only the writing systems you expect. For example, allow English only and messages in Cyrillic, Chinese, Arabic or other alphabets are rejected. It reads the alphabet, not the language, so it cannot tell two languages that share the same letters apart.
Tamper detectionSubmissions that do not match the original form are rejected and logged as spam. See server-side validation above.
Upload safetyFile uploads are limited to allowed types and sizes, executables and scripts are always refused, and the file content is checked.

Behind Cloudflare or another proxy, set the IP source in the Settings tab so that each visitor is seen as themselves rather than as the proxy. You can also change the messages visitors see when they are blocked, flagged or over the limit. The same spam protection covers Tuli's login, registration and password reset forms.

Other form elements

The element library also has ready-made Login, Registration, Password reset and Search forms, plus the WordPress comment form. They are normal elements you can style visually, and the server handles them the same careful way. AI agents and the AI assistant can create and edit forms through the same HTML.

Custom fonts

Upload your own font files and Tuli serves them from your own site. This is one of the easiest ways to make a site load faster and look exactly the way you designed it.

How it works

  1. Go to Tuli → Custom Fonts and click Bulk Upload Fonts (or Add New Font for a single family).
  2. Select your font files. Tuli accepts .woff2, .woff, .ttf and .otf, and checks that each file really is a font.
  3. Tuli reads the family name, weight and italic style from the file names, for example roboto-v51-latin-700italic.woff2 or OpenSans-BoldItalic.ttf. If your files have unhelpful names, use Add New Font to set the family and each weight by hand.
  4. Tuli writes the @font-face CSS for you. The font appears in the builder font picker and works in any CSS with font-family: "Roboto".

Why it helps page speed

  • No third-party request. Google Fonts makes the browser connect to another server before text can be drawn. Self-hosted fonts load from your own domain (and your CDN, if you use one), so there is one less connection to wait for.
  • No extra stylesheet. The generated @font-face rules are built into the global stylesheet that already loads on every page, so custom fonts add no extra CSS request.
  • No double loading. If you upload a font that is also on Google Fonts, your uploaded copy replaces it. Tuli never loads both.
  • Only what you use. Upload just the weights and styles your design needs, in .woff2 format, which is the smallest. Fewer files means fewer bytes.
  • Better privacy. Visitors do not contact Google just to see your text, which also simplifies cookie and privacy compliance.

Google Fonts still work if you prefer them: pick one in the Style panel and Tuli loads it automatically. To use only your uploaded fonts, hide the Google Fonts picker in Tuli → Settings → General. CSS that already references a Google font keeps working.

Code snippets

Tuli has a code snippets manager built in, so you do not need a separate snippets plugin or another plugin for each small feature. Open Tuli → Code Snippets to add PHP, HTML, CSS or JavaScript from one screen, without editing functions.php or building a plugin.

What you can add

  • PHP: runs like code in functions.php. Choose whether it runs everywhere, on the front end only, or in the admin only.
  • HTML, CSS and JavaScript: printed in the site header, right after the opening body tag, or in the footer. Tuli adds the style and script tags for you.
  • Give each snippet a clear title, switch it on and off, and search or filter the list by type.

Small features without extra plugins

Anything that is a small piece of custom behaviour can be a snippet, and the AI can write it for you. Describe what you want to the AI assistant or your MCP agent and it saves the PHP as a snippet:

Add a snippet that registers a [recent_posts] shortcode showing the latest
3 posts as links.
Add a snippet that redirects /old-pricing to /pricing with a 301.
Add a snippet that disables comments on all pages.
Add a snippet that changes the WooCommerce add to cart button text.

Typical uses are custom shortcodes, hooks and filters, redirects, small WooCommerce tweaks, form or email adjustments, tracking code, and custom functions you print with {tokens}. Each feature lives in its own titled snippet, so it is easy to find, read, switch off or delete later, instead of being buried in a theme file or spread across plugins. That keeps your site lighter and avoids installing a plugin for something that only needs a few lines.

Safe by design

  • Safe mode: Tuli remembers which PHP snippet is about to run. If it causes a fatal error, that snippet is switched off automatically and your site recovers on the next load instead of showing a white screen.
  • Syntax check: a PHP snippet with a syntax error is saved as inactive, so it can never take the site down. Fix it and switch it on again.
  • Permissions: saving PHP requires an administrator with permission to add unfiltered code. You can also turn off PHP execution completely in Tuli → Settings → Security.

Need something bigger? Build a custom plugin

A snippet is the right home for a single function, shortcode or tweak. If you need more complex functionality, such as several files, its own settings screen, custom database tables, or logic that must keep working if you change themes, ask Tuli to build a complete custom plugin instead. With the MCP server (or the AI assistant with the Code execution action allowed) the AI can create the plugin files, install and activate the plugin. Tuli applies a simple rule of thumb:

You need…Use
One function, shortcode, hook, redirect or small tweakA code snippet
A function whose output you print on a pageA code snippet plus a dynamic data token
A multi-file feature, its own admin screen, or logic that must survive a theme switchA custom plugin built by the AI

Header, body and footer code

Some code has to appear on every page of the site: analytics, a tag manager, search-console verification, a tracking pixel, a chat widget. You don't edit each page for this. Open Tuli → Settings → Header, Footer & Body, paste the code into the right box and save. It is added to every front-end page on your site.

The three boxes

BoxWhere it is printedTypical use
Header codeInside the <head>, near its end (WordPress's wp_head, priority 99).Google Analytics (gtag), Google Tag Manager's script, Search Console / Bing / Pinterest verification <meta> tags, Meta pixel, extra <link> tags such as preloads.
Body codeDirectly after the opening <body> tag (wp_body_open).Google Tag Manager's <noscript> fallback, skip links, a top announcement bar that must come first.
Footer codeJust before the closing </body> (wp_footer).Chat widgets, cookie banners, heatmap and tracking scripts that don't need to block the first paint.

Each box prints exactly what you typed, in the order WordPress loads it. Put anything that doesn't have to run before the page is visible in the footer box, so it never delays the first paint.

Examples

<!-- Header code: Search Console verification -->
<meta name="google-site-verification" content="YOUR_TOKEN">

<!-- Header code: Google Analytics 4 -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', 'G-XXXXXXX');
</script>

<!-- Body code: Tag Manager noscript fallback -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXX" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>

Rules to know

  • Site-wide, front end only. The code is added to the pages visitors see on your site.
  • Who can save it. Only administrators (manage_options) can open this screen. Raw scripts and meta tags are kept only for users with the unfiltered_html capability (a normal single-site administrator). On setups where that capability is restricted, such as multisite site managers, the code is filtered down to safe post-level HTML and <script> tags are removed on save.
  • Printed unchanged. There is no escaping or de-duplication when it is printed, because it was checked when you saved it. If a plugin already adds the same analytics tag, you will count every visit twice, so remove one of them.
  • Mistakes affect every page. An unclosed tag in the header box can break the whole site's layout. After saving, load a page and check it. If your site uses a caching plugin or a CDN, clear it so visitors get the new code.
  • It works with any theme-independent tag: you can keep all third-party tags in one place, and switch them off by clearing the box.

Which place for which code?

You want to add...Put it in...
A third-party tag or snippet (analytics, pixel, widget, verification)Header, Body or Footer code
Your own JavaScript for the whole siteGlobal JS in the builder. It is saved as tuli-global.js and loaded in the footer on every page.
JavaScript for one page onlyPage JS in the builder (tuli-{id}.js, only requested on that page).
CSSGlobal CSS for classes, Page CSS for one page (see Visual builder).
Code on specific pages only, or inside PHP logicA Code Snippet, which can target where it runs.

Because page HTML is sanitized for safety (scripts in page markup are stripped by default), the header/footer boxes and the JS boxes are the right homes for scripts.

Let the AI do it

The AI Assistant and MCP can read and set these three boxes for you (the set_header_footer_code tool). Give it the tag and say where it goes:

Add my Google Analytics 4 tag G-XXXXXXX to the header code and the Tag Manager noscript fallback GTM-XXXX to the body code. Don't overwrite anything already there.

Performance and security

Tuli's output is plain HTML and CSS, so there is no page-builder framework running in your visitors' browsers. On top of that the theme is built to ship only what a page uses. This section lists what Tuli does automatically, what you can switch on, and what stays in your hands.

What Tuli does automatically

  • Plain, static files. Your CSS and JS are written to real files in wp-content/uploads/tuli/ and served by the web server, so they cache like any static asset. They are rewritten when you save. If the folder isn't writable, Tuli falls back to a virtual URL that returns identical content.
  • One site stylesheet. On the public site the theme baseline and your Global CSS are combined into a single tuli-global.css (one request). Page CSS and Page JS files are requested only on pages that have some; an empty file is never linked.
  • Runtime scripts only when used. Behaviour for the menu, forms, sticky header, accordion and tabs, and popups is bundled into one small inline script, and each part is included only if the page (or its header and footer templates) uses that element. A page with none of them ships no runtime script and makes no extra request.
  • Heavy libraries only when used. The slider library (Swiper) loads only on pages with a slider, scroll animations (AOS) only on pages with animated elements, and the Font Awesome icon font only on pages that use icons.
  • Fonts load lean. Google Fonts are requested only for the families a page really uses, with preconnect hints and display=swap. Uploaded custom fonts are served from your own site, with the @font-face rules built into the global stylesheet and font-display: swap, so there is no third-party request at all.
  • Cheap loops. A query loop only counts total results when a pager or filter needs them, so plain loops skip that database work.
  • No inline styles. Styles live in the Global and Page CSS files rather than in every tag, so the HTML stays small and cacheable.

Turn on minification

In Tuli → Settings → General → Performance, tick Minify CSS & JS. Tuli then serves minified versions of all its generated files: the theme and global CSS, the per-page CSS and JS, the global JS, and the custom-font CSS. When you tick or untick it, every file is regenerated at once.

  • The minifier is deliberately conservative. It keeps strings and calc() spacing intact, and for JavaScript it removes comments and blank space but keeps line breaks, so your code behaves exactly as written.
  • The minified files are served to everyone, administrators included, so if your own custom code ever breaks when minified you notice immediately instead of only your visitors.
  • Your original code stays readable in the builder boxes; only the served file is minified.

Fonts setting

In Tuli → Settings → General → Fonts, Disable Google Fonts in the builder hides the Google Fonts picker so only your uploaded fonts can be chosen. Pages that already reference a Google font keep loading it, so nothing existing breaks. Use it to keep a site fully self-hosted.

Choose which post types get the builder

In Tuli → Settings → General → Page Builder, tick the post types that show the "Edit with Tuli" entry points. Pages and Templates are on by default; add posts or custom post types when you need them.

Security switch: turn PHP off

Under Tuli → Settings → General → Security, Disable PHP code execution in the builder turns off every place the builder can run PHP: PHP blocks, PHP-based query loops and similar. The default is on, because some sites use them. If your site is built only from HTML, CSS, dynamic data tokens, forms and templates, turn it off for a smaller attack surface. Dynamic data tokens, query loops built with the fields, and forms keep working.

Other protections are always on: page HTML and CSS are sanitized, scripts in page markup are removed by default, saved PHP is signed so a database edit cannot inject runnable code, and only users with the right capabilities (administrator plus unfiltered_html) can save PHP or raw script code.

What stays up to you

Because Tuli gives you full control of the HTML, the biggest wins are small attributes you can set (or ask the AI to set) on your own tags:

  • Images: compress and size them correctly, set width and height, add loading="lazy" to images below the fold, and use fetchpriority="high" on the main hero image.
  • Third-party tags: every analytics script, widget and pixel in the header and footer boxes costs speed. Keep only what you use, and prefer the footer box.
  • Plugins: use Code Snippets and custom fonts instead of installing a plugin for each small feature.
  • Caching and CDN: use a caching plugin or your host's cache, and a CDN for images. Clear the cache after you change code.

Ask the AI for an audit:

Check the home page for speed: find large images, missing width and height, images that should be lazy loaded, unused fonts and heavy third-party scripts, then fix what you can and list the rest.

WooCommerce

With WooCommerce active, Tuli lets you design every part of a shop visually and with AI, without touching WooCommerce template files. Instead of overriding templates in a plugin, you assemble pages from small WooCommerce building blocks. Each block prints real WooCommerce output, so carts, checkout, payments, stock, tax and shipping all keep working exactly as WooCommerce handles them. Step by step guide: Build a WooCommerce store with AI.

Only when WooCommerce is active

All of this is switched on only when WooCommerce is installed and active. On a site without a shop the WooCommerce elements don't appear in the picker, and none of the shop CSS, JavaScript or hooks are loaded.

How the building blocks work

A WooCommerce element is a tiny HTML marker, for example <div data-tuli-woo="price"></div>, with its settings stored as data-tuli-woo-* attributes. When the page is shown, Tuli replaces the marker with the live WooCommerce output. The marker stays small and clean in your saved HTML, and the same marker works on the editor canvas and on the live site. In the editor, product parts show a sample product so they are never empty while you design.

  • Product parts read the product in context: the product page being viewed, or the current item inside a product query loop. Put a Product Title, Price and Add to Cart inside a loop card and each card shows its own product.
  • Page parts (cart, checkout, grids, header cart) don't need a product and work anywhere.
  • You can style them with normal classes in the Style tab, and the Content tab holds each part's own options.

The elements

GroupElements
Single productProduct Title (choose the heading tag, optional link), Product Price, Add to Cart, Product Gallery, Product Rating, Short Description, Product Description, Product Tabs, Additional Info, Product Meta, Product SKU, Stock Status, Product Categories, Product Tags, Sale Badge, Related Products, Up-sells, Cross-sells.
Shop and archiveProducts Grid, Categories Grid, Result Count, Catalog Ordering, Archive Title, Breadcrumb.
Cart, checkout and accountCart, Checkout, My Account, Order Tracking.
Header and globalCart Icon (with count and optional total), Cart Count, Mini Cart, Product Search, Store Notices.

Options worth knowing

  • Add to Cart: choose a button (the loop-style button) or the full form (quantity and variation pickers), custom button text, and AJAX add on or off. With AJAX on, items are added without a reload for simple products and for the full form, including variable products, and the header cart updates itself. Turning AJAX off removes it for that element even if WooCommerce's global AJAX setting is on.
  • Product Gallery: thumbnails position, sale badge, hover zoom, a zoom icon, a full-screen lightbox with slider and thumbnails, and the zoom level. The lightbox loads its slider library on product pages automatically.
  • Product Price, Stock, Tags, Rating: colours, sizes and labels (for example your own "In stock" / "Out of stock" / "On backorder" text and colours) in the Content tab.
  • Products Grid: number of products, columns, type (all, featured, on sale, best selling, top rated), categories, tags, specific product IDs, order by and direction. Categories Grid: how many, columns, order, parent, hide empty, specific IDs.
  • Related, Up-sells, Cross-sells: an optional heading, heading size and colour, how many to show, and columns. Cross-sells are cart based, like WooCommerce.
  • Cart, Checkout, My Account, Order Tracking, Mini Cart: recolour the whole block with accent, text, surface and border colours plus button and hover colours, without writing CSS.
  • Cart Icon / Mini Cart: ideal for the header template. Counts and totals refresh after an AJAX add through WooCommerce's cart fragments, and a full Cart or Checkout element on the same page re-syncs when an item is removed from the mini cart.
  • Product Search: custom placeholder, button text, or an icon button.
  • Store Notices: prints the live WooCommerce messages ("added to your cart", coupon errors). The preview choice only changes the sample shown in the editor.

Shop colours and look

Open Theme Settings → WooCommerce in the builder to set the shop palette once for the whole store: accent, text, muted text, border, surface and soft surface, star rating colour, success and error colours, corner radius, quantity buttons and add-to-cart button colours (normal and hover). Empty fields keep Tuli's built-in shop look. A single block can still override these with its own colours.

Build shop pages with templates

Combine the elements with Templates:

  • Archive template for products (the Shop and product categories): a Main Query loop with a card made of the featured image ({get_the_post_thumbnail_url}), Product Title, Price and Add to Cart, plus Result Count, Catalog Ordering, Archive Title, Breadcrumb and Pagination. The loop uses WooCommerce's own products-per-page by default (or your override), and the editor previews the same number of products as the live Shop.
  • Single template for the product post type: Gallery, Title, Rating, Price, Short Description, Add to Cart, Meta, Tabs and a Related Products loop, laid out any way you like. Set a preview product so the editor shows real data.
  • Cart, Checkout and My Account pages: drop the Cart, Checkout and My Account elements on ordinary pages and style the page around them.
  • Header template: Cart Icon or Mini Cart, and Product Search.
  • Filters: add the native Filter element (category, price range, search, sort) to the shop archive, or use WP Grid Builder facets.

What Tuli sets up for you

  • Declares WooCommerce theme support, including product gallery zoom, lightbox and slider, and sensible image sizes (you can still change them in WooCommerce settings).
  • Loads its own self-contained shop stylesheet and script on the site, so styles work even on a normal page that only uses a few shop blocks.
  • Four products per row by default, a full-width layout without WooCommerce's default sidebar, and quantity − / + buttons that are in the page from the first paint.
  • Removes WooCommerce's built-in "You may be interested in…" block from the cart. Use the Cross-sells element where you want it.

Ask the AI to build the store

Build my shop with WooCommerce: a Shop archive template with a 3 column product grid (image, title, price, rating, Add to Cart), a category filter, price range filter, sort dropdown and pagination; a Single Product template with gallery, title, price, short description, add to cart with quantity, tabs and 4 related products; and a header with a mini cart.

Remember that products, payment gateways, shipping and tax are set up in WooCommerce itself. Tuli designs how they look.

ACF (Advanced Custom Fields)

Tuli works with Advanced Custom Fields (ACF) in four places: printing field values, looping over repeater rows, filtering by field values, and saving form answers into fields. The AI can also create the fields, custom post types and taxonomies for you. Everything is detected automatically when ACF is active, and nothing ACF related is shown when it isn't.

Print field values

Every top-level ACF field appears in the dynamic data picker (the database icon) with its label and type. Inserting one writes a token such as:

<h2>{the_field:headline}</h2>
<p>Price: {the_field:price}</p>
<a href="{the_field:booking_url}">Book now</a>
  • On a single template it reads the post being viewed. In a query loop it reads the current result. Set a preview post on the template so the editor shows real values.
  • Fields on an Options page or on a specific post: ACF's field function accepts a second argument, so {the_field:company_phone,option} or {the_field:price,123} work.
  • Images, files and links: tokens print text. For an image field used in src, set the field's return format to Image URL in ACF so the token prints a usable address (the array and object formats can't be printed as a single value).
  • The token {the_field:name} is what Tuli allows. If you need another ACF function, register it as a custom token, as described in Dynamic data.

Loop over repeater rows

  1. Design one row, for example a pricing card.
  2. Turn on Query loop, choose ACF Repeater (enabled only when ACF is active) and type the repeater's Field name.
  3. Choose where the rows live: the Current post, a Specific ID (enter the post ID) or an Options page.
  4. Inside the card print each row's sub-fields with {the_sub_field:sub_field_name}.
<div class="plan">
  <h3>{the_sub_field:plan_name}</h3>
  <p class="price">{the_sub_field:price}</p>
</div>

If the repeater is empty, or ACF isn't active, the loop renders nothing. Great uses: pricing tiers, FAQs, team members, feature lists and galleries.

Filter and sort by ACF fields

ACF stores values as post meta under the field name, so you can use them in query loops and filters:

  • In a WP_Query loop, add a meta row with the field name as the key, for example featured equals 1, or price greater than 100.
  • The native Filter element has a Field type: give it the ACF field name and it becomes a dropdown, checkboxes, radio buttons, a text search or a min/max range slider (for example a price).
  • A taxonomy created in ACF can be used by a Taxonomy filter and in a loop's taxonomy rows like any other taxonomy.

Save form answers into ACF fields

In Tuli → Forms, the Create a post and Create a user actions have custom-field rows. With ACF active each row can pick one of your ACF fields from a list (grouped by field group) and map a form field to it. Tuli stores it the way ACF expects, so the value reads back correctly through ACF and through {the_field:name}. Without ACF you can type any meta key instead. See Forms.

Let the AI create fields, post types and taxonomies

For custom post types, taxonomies and custom fields the AI uses ACF by default, so everything stays editable in the WordPress dashboard instead of hidden in code. If ACF isn't installed yet it can install and activate it first. Over MCP or in the AI Assistant, with the Custom post types, taxonomies & ACF fields action ticked, it can:

  • create a field group (edit it later at ACF → Field Groups), a custom post type (ACF → Post Types) and a taxonomy (ACF → Taxonomies);
  • read all ACF values of a post, and set a field's value on a post;
  • build the templates, loops and dynamic-data tokens that display the result.
Create a "Team member" custom post type with ACF fields for role, photo (return Image URL), bio and LinkedIn URL. Then build a Team page with a loop of all team members as cards, and a single Team member template showing those fields.

WP Grid Builder

If you use the WP Grid Builder plugin for faceted filtering, Tuli can place its facets and grids on your pages and make them filter Tuli's own loops. It is optional: Tuli already has a native Filter element that needs no plugin. WP Grid Builder's elements appear in the picker only when the plugin is active. If the plugin is later removed, the markers are cleaned from the live pages instead of printing broken output.

The two elements

ElementWhat it doesSettings
FacetPrints one of your WP Grid Builder facets: checkbox, radio, select, search, range, color, rating, hierarchy, A-Z index, sort, per-page, reset, apply, result count, selection, pagination, load more and so on.Pick the facet from your list, optionally a facet style (filtered to the styles that match the facet type), and the target it filters.
GridPrints a complete WP Grid Builder grid, cards and layout included, so it shows in the editor as well as on the live site.Pick the grid from your list.

What a facet can filter

Choose the facet's target:

  • Main query (default): the archive, shop or search results of a Tuli Archive template that uses a Main Query loop. Put the facet in that template. Tuli wraps the loop for you so WP Grid Builder can swap the filtered cards without a page reload.
  • A Tuli WP_Query loop: pick the loop by its query id. Tuli tags the loop's query for WP Grid Builder, so the facet filters that loop. This needs WP Grid Builder's "Filter custom content" option enabled in its settings.
  • A native WP Grid Builder grid: choose "grid:ID" to bind the facet directly to a Grid element or a grid shortcode on the page.

In the editor

  • Search, sort and per-page style facets render for real. Facets that WP Grid Builder builds in the browser from a grid's results (pagination, selection, result count, load more) show a representative sample so you can place and style them.
  • If a native WP Grid Builder grid is on the page, such facets bind to it live in the editor. A facet that targets the main query or a Tuli loop shows the sample, since those aren't WP Grid Builder grids.
  • Facet colours come from the facet styles you made in WP Grid Builder. Tuli loads that facet CSS in the page head (not late in the footer), so the facet is correctly coloured on first paint instead of flashing.

When to use which

Native Filter elementWP Grid Builder
Needs a pluginNoYes (WP Grid Builder)
Filter typesTaxonomy, custom field, search, sort, paginationMany more facet types, with its own admin and styling
Page weightVery lightLoads the plugin's scripts and styles
Best forMost blogs, directories and shopsSites already built around WP Grid Builder, or needing its advanced facets

Create the facets, facet styles and grids in WP Grid Builder first, then ask the AI to wire them in: "Add my 'Category' and 'Price' WP Grid Builder facets above the product loop on the Shop archive template, targeting the main query."

Ongoing changes

A launched site is never finished. The same AI you used to build it can keep it up to date. Everything below works with the AI Assistant or any connected MCP agent. Search visibility, answer engines and accessibility each have their own sections after this one: SEO, AEO and ADA.

Make ongoing changes

  • Describe the change: open Tuli → AI Assistant (or the assistant panel on a page) and say what you want, for example "Update the opening hours on the Contact page and in the footer" or "Add a new Gluten-free section to the Menu page".
  • Be specific about where: name the page, or attach it with @, so the AI changes the right thing. For site-wide changes (header, footer, brand colours) say so.
  • Fine-tune visually: small tweaks to spacing, colour and text are quickest in the style panel. The AI and the visual builder edit the same HTML and CSS, so you can switch between them freely.
  • Review before saving: check the preview on desktop, tablet and mobile, then click Save. Nothing is saved until you do.
  • Add new content: ask the AI to write and publish blog posts, add landing pages, or create a new template, form or custom post type.
  • Keep control: after the initial build, you can untick powerful actions (such as plugin installs and code execution) in the AI Assistant site tools or on the MCP application password, and revoke passwords you no longer use.

SEO

SEO (search engine optimisation) is how easily Google and Bing can find, understand and rank your pages. Tuli helps because the pages are clean, real HTML: headings, links, images and text are exactly the tags you wrote, with no builder wrappers or scripts hiding them from crawlers, and the server sends them as finished HTML.

Why Tuli suits SEO

  • Semantic, readable markup. Use real <h1> to <h3>, <nav>, <main>, <article> and <a> tags. What you design is what crawlers read.
  • Speed. Pages ship only the CSS, JS and fonts they use. See Performance and security, then turn on Minify CSS & JS and use local custom fonts.
  • Dynamic pages stay crawlable. Query loops, dynamic data and filtered URLs are rendered on the server, so lists and values are in the HTML that crawlers fetch.
  • Full control of attributes. Set alt, title, rel, loading, width and height on any tag.
  • Templates keep it consistent. Build the header, footer and single-post layout once in Templates so headings, breadcrumbs and links are the same on every page.

Improve SEO with the AI

Try prompts like these, one page at a time or across the whole site:

Audit every published page for SEO. For each page, list the title, meta
description, H1, and any missing or duplicate ones, then fix them.
Write unique titles under 60 characters and meta descriptions under 160.
  • Titles and descriptions: ask the AI to write them. If you don't have an SEO plugin, ask it to install and set one up, then fill in titles, descriptions and the XML sitemap.
  • Headings and structure: one H1 per page, headings in order (H2, then H3), descriptive text for links instead of "click here".
  • Images: ask for descriptive alt text on every image and sensible file sizes. Use WebP where you can.
  • Internal links: ask the AI to link related pages and posts to each other.
  • Speed: turn on Minify CSS & JS and use local fonts. Details are in Performance and security.
  • Analytics and verification: paste Google Search Console verification tags and analytics snippets into Header, body and footer code, or ask the agent to add them.

SEO checklist per page

ItemWhat to aim for
Title tagUnique, under about 60 characters, main topic first.
Meta descriptionUnique, under about 160 characters, written to earn the click.
H1Exactly one, matching the page topic.
URLShort, readable slug.
ImagesDescriptive alt, compressed, correct width and height.
LinksDescriptive link text, internal links to related pages, working links only.
IndexingAn XML sitemap, a sensible robots.txt, and the site submitted in Search Console.

After changes, check results in Search Console and an audit tool such as Lighthouse. Rankings take time and no tool can guarantee a position.

AEO (answer engine optimisation)

AEO means making your content easy for AI assistants and search answer boxes (ChatGPT, Claude, Gemini, Perplexity, Google AI Overviews) to understand and quote.

Rework the Home, About and Services pages for answer engines. Add a short
FAQ section with real questions customers ask, each with a clear 1 to 3
sentence answer. Add matching FAQPage JSON-LD and Organization JSON-LD.
  • Question headings with direct answers: turn key topics into "What is…?", "How much does…?" and "How do I…?" headings, followed by a short, factual answer.
  • Structured data: ask the AI to add JSON-LD schema (Organization, LocalBusiness, Product, FAQPage, Article) to the page or to header code.
  • Clear facts: state who you are, what you do, where, prices and contact details in plain HTML text, not only in images.
  • AI crawler files: an MCP agent with the Code execution action can create files such as llms.txt, and you can review your robots.txt to make sure the AI crawlers you want are allowed.
  • Keep it fresh: ask the AI to review and update key pages on a schedule, such as every quarter.

Why Tuli suits AEO

  • Answers, FAQs and facts are plain HTML text, so AI crawlers read them without running scripts.
  • Because you control every tag, you can mark up question headings, lists and tables that answer engines like to quote.
  • Use dynamic data and query loops to publish many consistent answer pages from your own content, such as one page per service or location.
  • JSON-LD structured data can go in Header code for the whole site, or in a Code Snippet that prints per-page schema.

ADA and accessibility

See also SEO: most accessibility fixes (alt text, headings, link text, landmarks) help search engines too.

Accessibility lives in the HTML. Most fixes mean adding or correcting attributes on specific tags: alt on images, aria-label on icon buttons, for and id pairs on form labels, role and landmark tags, lang on the page, and aria-expanded on menus and accordions. Tuli is a strong fit for this because every element is a real HTML tag and you have complete control over its tags and attributes. There is no hidden builder markup in the way, no wrapper divs you cannot reach, and nothing stripped out of your attributes. You can set them in the Content tab, edit the HTML directly, or ask the AI to add them across the whole site.

Attribute or tagWhat it does for accessibility
alt on imagesGives screen readers a text description. Use an empty alt="" for purely decorative images.
aria-label and aria-labelledbyNames buttons, icon links, menus and regions that have no visible text, such as a search icon or a hamburger button.
aria-expanded, aria-controls, aria-currentTells assistive technology whether a menu, accordion or tab is open, what it controls, and which page link is current.
aria-live and role="alert"Announces form errors, success messages and other updates without a page reload.
role and landmark tagsUsing <header>, <nav>, <main> and <footer> lets keyboard and screen reader users jump between page regions.
for and id on labels and inputsConnects each form label to its field so it is read aloud and the label is clickable.
tabindexControls keyboard focus order and makes custom controls reachable. Avoid positive values.
langTells screen readers which language to pronounce, including on passages in another language.
href="#main" skip linkLets keyboard users jump past the menu straight to the page content.

Try a prompt like this to have the AI apply them:

Go through every page and template and add the missing accessibility
attributes: alt text on images, aria-label on icon-only buttons and links,
aria-expanded on menus and accordions, label/for pairs on form fields, and
landmark tags (header, nav, main, footer). Add a skip-to-content link to
the header template.
Audit the whole site against WCAG 2.2 AA. Check colour contrast, image
alt text, form labels, heading order, link text, keyboard focus styles,
and page landmarks (header, nav, main, footer). Fix what you can in the
HTML and CSS and list anything that needs my decision.
  • Text and colour: sufficient contrast, readable font sizes, and no information conveyed by colour alone.
  • Images and media: alt text for meaningful images, empty alt for decorative ones, captions or transcripts for video and audio.
  • Forms: a visible label for every field, clear error messages, and required fields marked in text.
  • Keyboard use: every link, button, menu and popup works with the keyboard, has a visible focus outline, and the page has a skip-to-content link.
  • Structure: proper landmarks and heading order, and descriptive link and button text.
  • Motion: respect prefers-reduced-motion for animations and sliders.

AI can fix most common issues quickly, but it cannot guarantee legal compliance. After the AI pass, run an automated checker (such as WAVE, axe or Lighthouse), test with the keyboard and a screen reader, and consider publishing an accessibility statement. Check the rules that apply to your country and industry.

Export your site as static HTML

Open Tuli → Settings → Export Site to download your site as a zip of plain HTML, CSS, JS and assets. Tuli crawls the public front end exactly as a visitor sees it, saves each page, downloads the assets it uses and rewrites links to local copies. Dynamic output such as blog listings and query loops is captured as rendered HTML. It's useful for static hosting, handoff to a client, or archiving.

Support

Licences include lifetime updates and support. Watch the video tutorials, check the FAQ, or contact us. Manage your licences, sites and downloads in the Customer Portal.