Configuration
Langeto has one settings screen and a deliberately small set of options. Everything lives under WooCommerce > Langeto Tabs, stored in a single tabby_settings option. The save capability is manage_woocommerce, so shop managers can edit tabs without a full admin account.
Enable Langeto
Section titled “Enable Langeto”The master switch in the General section (enabled), on by default. Turn it off to hide every tab on the storefront at once without deleting them, your tabs stay saved and reappear when you turn it back on. While off, Langeto contributes no tabs and does not enqueue its front-end stylesheet.
Shortcodes and blocks in tabs
Section titled “Shortcodes and blocks in tabs”rich_content, off by default. Tick it and a tab body is run through the_content before it is printed, which is what makes shortcodes and block markup inside a tab actually execute.
With it off, a tab body is sanitised with wp_kses_post and paragraphed with wpautop, so a shortcode is printed as the literal text you typed. With it on, the_content runs instead: shortcodes execute, blocks render, and every filter your theme and other plugins have attached to post content applies to the tab too.
It ships off because the_content over stored HTML is a wider surface than the safe-HTML pass, not because the tab bodies are untrusted: only a user with manage_woocommerce can write them, and only the tabs you wrote yourself are affected. Turn it on when you need a shortcode in a tab; leave it off otherwise.
The checkbox is the default value of the tabby/use_rich_tab_content filter, so an add-on can answer per tab or per product. Note that Langeto Pro takes the decision over entirely: while Pro is active its own rich-content switch is the answer, so a ticked box here does nothing if that switch is off, and vice versa. With Pro installed, set it there.
The Tabs section is a repeating list. Each row is one reusable tab and has three fields:
- Tab title, the label in the product tab strip and the heading printed above the tab content. Sanitised with
sanitize_text_fieldon save. A row with a blank title is dropped on save, so leaving a row empty is how you discard it. - Enabled, a per-tab toggle. Unticked rows are saved but not rendered on the storefront, so you can park a tab without losing its content.
- Tab content, the body, sanitised with
wp_kses_poston save (see Allowed HTML).
Tabs render on every single product page, in the order listed on this screen, after the native WooCommerce tabs. Use Add tab to append a row and the × button to remove one; the list is reindexed automatically.
Tab slugs
Section titled “Tab slugs”On save, each tab is given a stable slug derived from its title with sanitize_key (for example Shipping & Returns becomes shipping-returns). If two tabs would produce the same slug, Langeto appends _1, _2 and so on so each stays unique. The slug is the tab’s internal key, used in the panel’s HTML id (tab-tabby_<slug>), and is not separately editable.
Allowed HTML in tab content
Section titled “Allowed HTML in tab content”Tab content is always sanitised with WordPress’s wp_kses_post on save, whatever the rich-content setting says. On render the same pass runs again unless Shortcodes and blocks in tabs is on, in which case the_content takes over. The safe subset is the one WordPress allows in post content:
- Links, ordered and unordered lists
- Headings, paragraphs, line breaks
- Bold, italic and other inline emphasis
- Images
- Blockquotes and similar block elements
Scripts, iframes and other unsafe markup are stripped on save, so they never reach the database. On render with rich content off, the stored content is sanitised once more and passed through wpautop, so plain line breaks become paragraphs the way they do in a post.
What is fixed by design
Section titled “What is fixed by design”A few behaviours are deliberately not settings:
- Tabs always render after the native WooCommerce tabs (the filter runs at priority
98, and each tab is added at priority100and up). There is no before/after placement option. - Tabs always appear on every product. There is no per-product tab editor and no per-product hiding in the free plugin.
- A tab with a title but empty content is not registered at all: it never reaches the tab strip, so a shopper cannot click through to a heading with nothing under it. The panel repeats the check on the formatted HTML, so content that sanitises down to nothing is dropped as well.
Storage and cleanup
Section titled “Storage and cleanup”All configuration lives in one option, tabby_settings (the master toggle, the rich-content toggle and the global_tabs array). Langeto creates no custom database tables and writes no post meta. A second option, tabby_db_version, records the schema version so future updates can migrate safely.
Deleting the plugin from the Plugins screen runs the uninstall routine, which removes both tabby_settings and tabby_db_version. Nothing is sent to any external service.
Theming the tab panels (CSS)
Section titled “Theming the tab panels (CSS)”Langeto’s panels are styled with --tabby-* custom properties, the index-divider edge colour, its width, the reserved gutter and the open-tab animation timing, scoped to panels whose id starts with tab-tabby_. Override them from your theme stylesheet to re-skin Langeto without touching the native panels:
.woocommerce-tabs .panel[id^="tab-tabby_"] { --tabby-edge: #2563eb; /* the index-tab edge colour */ --tabby-edge-width: 3px; /* its thickness */}The panels carry a built-in dark-scheme palette (via prefers-color-scheme) and the short “file into place” open animation is disabled automatically under prefers-reduced-motion. Langeto ships no theme-overridable PHP template, the panel markup is a heading and a content <div>, restyled entirely through these CSS tokens.
Extending Langeto (developers)
Section titled “Extending Langeto (developers)”After Langeto gathers the enabled tabs for a product it passes them through one filter before WooCommerce renders them:
add_filter( 'tabby/resolved_tabs', function ( array $tabs, ?WC_Product $product ): array { // $tabs is an array of Langeto\Domain\Tab value objects, in render order. // $product is the product being rendered, when WooCommerce has set it.
// Example: hide a tab on a specific product. if ( $product && 123 === $product->get_id() ) { $tabs = array_filter( $tabs, fn ( $tab ) => 'warranty' !== $tab->id ); }
return array_values( $tabs );}, 10, 2 );Each item is a Langeto\Domain\Tab with readonly id, title, content, enabled and source properties. Anything you return that is not a Tab instance is dropped before rendering, so the renderer never receives a foreign shape. This is the same hook Langeto Pro’s category-scoping uses. (The tabby/booted action is an internal boot signal for add-ons, not a content hook.)