mcpField
T
theme-snippets

theme-snippets

github.com/ahmed-lakoshaDesign & UI7663 installsUpdated 31w ago
Repository

About theme-snippets

A reference skill for building Odoo website snippets, covering static and data-driven templates, customization options, and version differences. It is intended for developers creating or adapting snippets across Odoo versions.

Key features

  1. 01Catalog of static snippet templates
  2. 02Dynamic snippet data flow
  3. 03Snippet option elements and attributes
  4. 04JavaScript handlers and widgets
  5. 05Odoo 14–19 version differences

Use cases

  1. 01Choosing a snippet for a landing page or FAQ
  2. 02Registering a custom snippet in an Odoo module
  3. 03Configuring editable controls for a snippet
  4. 04Building a data-driven product display

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/ahmed-lakosha/odoo-plugins --skill theme-snippets

After installing, type/theme-snippetsin Claude Code to confirm it loaded.

Details

From the project README

Odoo Website Snippets Reference

Snippet Architecture

STATIC SNIPPETS (81+ templates)    DYNAMIC SNIPPETS (data-driven)
├── Pre-built HTML                  ├── ir.filters (data source)
├── Drag & drop                     ├── website.snippet.filter (fetcher)
└── 6 categories                    └── Display templates (renderer)

              SNIPPET OPTIONS SYSTEM
              we-select | we-button | we-colorpicker | we-input
              data-selector | data-select-class | data-js

Static Snippets Inventory (81+ Templates)

Structure (14)
IDNameBest For
s_bannerBannerLanding pages
s_coverCoverHeaders
s_text_imageText - ImageFeatures
s_image_textImage - TextFeatures
s_titleTitleSection headers
s_text_blockText BlockContent
s_numbersNumbersAchievements
s_three_columnsColumnsContent grids
s_featuresFeaturesBenefits
s_masonry_blockMasonryGalleries
s_image_galleryImage GalleryPortfolios
Features (11)
IDNameBest For
s_comparisonsComparisonsProduct comparison
s_company_teamTeamAbout pages
s_call_to_actionCTAConversions
s_referencesReferencesSocial proof
s_accordionAccordionFAQs
s_features_gridFeatures GridBenefits
s_pricelistPricelistProducts
s_faq_collapseFAQSupport
s_tabsTabsOrganization
Dynamic Content (15)
IDNameBest For
s_google_mapGoogle MapContact pages
s_website_formFormLead capture
s_social_mediaSocial MediaEngagement
s_dynamic_snippetDynamicData display
s_countdownCountdownEvents
s_popupPopupPromotions
s_newsletter_blockNewsletterMarketing
Inner Content (16)
IDNameBest For
s_cardCardItems
s_three_cards / s_four_cardsCardsServices/Features
s_timelineTimelineHistory
s_process_stepsStepsTutorials
s_quotes_carouselTestimonialsSocial proof
s_blockquoteBlockquoteEmphasis
Mega Menus (9)
IDName
s_mega_menu_multi_menusMultiple menu columns
s_mega_menu_cardsCard-style menu
s_mega_menu_big_iconsLarge icon menu
s_mega_menu_thumbnailsThumbnail gallery

Creating Custom Snippets

Odoo 14-17: Simple Registration
<!-- 1. Snippet Template -->
<template id="s_my_snippet" name="My Snippet">
    <section class="s_my_snippet pt48 pb48 o_cc o_cc1">
        <div class="container">
            <div class="row">
                <div class="col-lg-12 text-center">
                    <h2>My Custom Snippet</h2>
                    <p>Content goes here...</p>
                </div>
            </div>
        </div>
    </section>
</template>

<!-- 2. Registration -->
<template id="s_my_snippet_insert" inherit_id="website.snippets">
    <xpath expr="//div[@id='snippet_structure']//t[@t-snippet][last()]" position="after">
        <t t-snippet="my_module.s_my_snippet"
           t-thumbnail="/my_module/static/src/img/snippets/s_my_snippet.svg">
            <keywords>my, custom, snippet</keywords>
        </t>
    </xpath>
</template>
Odoo 18-19: With Snippet Groups (Required)
<!-- 1. Snippet Template (same as above) -->

<!-- 2. Custom Group (optional) -->
<template id="snippet_group_custom" inherit_id="website.snippets">
    <xpath expr="//div[@id='snippet_groups']" position="inside">
        <t snippet-group="custom"
           t-snippet="website.s_snippet_group"
           string="Custom Snippets"/>
    </xpath>
</template>

<!-- 3. Registration with Group -->
<template id="s_my_snippet_insert" inherit_id="website.snippets">
    <xpath expr="//div[@id='snippet_structure']/*[1]" position="before">
        <t t-snippet="my_module.s_my_snippet"
           string="My Snippet"
           group="custom"
           t-thumbnail="/my_module/static/src/img/snippets/s_my_snippet.svg"/>
    </xpath>
</template>
Panel Categories
Panel IDCategoryXPath
snippet_structureStructure//div[@id='snippet_structure']
snippet_mediaGallery//div[@id='snippet_media']
snippet_featureFeatures//div[@id='snippet_feature']
snippet_dynamicDynamic//div[@id='snippet_dynamic']
snippet_inner_contentInner Content//div[@id='snippet_inner_content']

Snippet Options System

We-* Elements
ElementPurposeKey Attributes
we-selectDropdowndata-name, data-dependencies
we-buttonToggledata-select-class, data-toggle-class
we-colorpickerColor pickerdata-css-property
we-inputText/numberdata-attribute-name, data-unit
we-rangeSliderdata-min, data-max, data-step
we-checkboxToggledata-select-class
Data Attributes
AttributePurpose
data-selectorTarget CSS selector
data-select-classToggle CSS class
data-css-propertyCSS property to modify
data-jsJavaScript handler class
data-dependenciesConditional visibility
data-no-previewDisable live preview
Complete Options Example
<template id="s_my_snippet_options" inherit_id="website.snippet_options">
    <xpath expr="." position="inside">
        <div data-js="MySnippetHandler"
             data-selector=".s_my_snippet"
             data-drop-in=".oe_structure">

            <we-select string="Layout" data-name="layout_opt">
                <we-button data-select-class="layout-grid">Grid</we-button>
                <we-button data-select-class="layout-list">List</we-button>
            </we-select>

            <we-colorpicker string="Background"
                            data-select-style="true"
                            data-css-property="background-color"/>

            <we-range string="Spacing"
                      data-select-style="true"
                      data-css-property="gap"
                      data-min="0" data-max="100" data-step="5" data-unit="px"/>
        </div>
    </xpath>
</template>
Built-in JS Handlers

BackgroundImage, BackgroundPosition, BackgroundToggler, ColoredLevelBackground, BackgroundShape, ImageTools, Carousel, DynamicSnippet


Dynamic Snippets

Architecture
Data Source (ir.filters) → Filter (website.snippet.filter) → Display Template
Creating a Dynamic Snippet

Step 1: Define Filter

<record id="dynamic_filter_my_items" model="website.snippet.filter">
    <field name="name">My Items</field>
    <field name="model_name">my.model</field>
    <field name="limit">12</field>
    <field name="filter_id" ref="ir_filter_my_items"/>
</record>

Step 2: Display Template

<template id="dynamic_filter_template_my_model_card" name="Card">
    <t t-foreach="records" t-as="record">
        <div class="col-lg-4 mb-4">
            <div class="card h-100 shadow-sm">
                <img t-att-src="record.image_url" class="card-img-top"/>
                <div class="card-body">
                    <h5 class="card-title" t-esc="record.name"/>
                </div>
            </div>
        </div>
    </t>
</template>

Step 3: Dynamic Snippet

<template id="s_dynamic_my_items" name="Dynamic My Items">
    <section class="s_dynamic_snippet s_dynamic_my_items pt48 pb48"
             data-snippet="s_dynamic_my_items"
             data-filter-id="my_module.dynamic_filter_my_items"
             data-template-key="my_module.dynamic_filter_template_my_model_card"
             data-number-of-elements="6">
        <div class="container">
            <div class="dynamic_snippet_template row"/>
        </div>
    </section>
</template>
Product Dynamic Snippets (website_sale)

Filters: dynamic_filter_newest_products, dynamic_filter_recently_sold_products, dynamic_filter_recently_viewed_products

Display templates: card_style, centered, horizontal_card, mini, minimalist_*, banner


Snippet JavaScript

publicWidget for Snippets
/** @odoo-module **/
import publicWidget from "@web/legacy/js/public/public_widget";

publicWidget.registry.MySnippet = publicWidget.Widget.extend({
    selector: '.s_my_snippet',
    disabledInEditableMode: false,

    start: function () {
        if (!this.editableMode) {
            this._initializeAnimations();
        }
        return this._super(...arguments);
    },

    destroy: function () {
        $(window).off('.mySnippet');
        this._super(...arguments);
    },
});
Snippet Options Handler
/** @odoo-module **/
import options from "@web_editor/js/editor/snippets.options";

options.registry.MySnippetOption = options.Class.extend({
    selectDataAttribute: function(previewMode, widgetValue, params) {
        this._super(...arguments);
        if (params.attributeName === 'layout') {
            this._applyLayout(widgetValue);
        }
    },
});

Version Differences

FeatureOdoo 14-17Odoo 18-19
RegistrationSimple XPathGroups required
JavaScript@odoo-modulePlugin-based (19)
Bootstrap4.x (14-15) / 5.1.3 (16-17)5.1.3
Dynamic SnippetsBasicFull + Categories
Odoo 19 Plugin Pattern
import { Plugin } from "@html_editor/plugin";
import { registry } from "@web/core/registry";

export class MySnippetPlugin extends Plugin {
    static id = "mySnippet";
    resources = {
        builder_options: [{
            template: "my_module.MySnippetOption",
            selector: "section",
            applyTo: ".s_my_snippet",
        }],
    };
}
registry.category("website-plugins").add("mySnippet", MySnippetPlugin);

Comments

Markdown supported