mcpField
O
odoo-frontend

odoo-frontend

github.com/ahmed-lakosha開発7648 回インストール31 週間前に更新
リポジトリ

odoo-frontend について

テーマ生成、バージョン検出、テーマ設定、フロントエンド開発手法を扱う Odoo フロントエンド開発スキルです。Odoo 14~19 の違いにも対応し、Odoo のウェブサイトやテーマを開発・保守する開発者向けです。

主な機能

  1. 01/create-theme でテーマモジュールを生成
  2. 02Odoo バージョンを検出して Bootstrap を対応付け
  3. 03テーマ変数と動的ページテンプレートを参照
  4. 04JavaScript、PWA、テスト、パフォーマンスを網羅
  5. 05Figma デザイン変換と Chrome DevTools のスタイル抽出

活用シーン

  1. 01Odoo ウェブサイトのテーマモジュールを作成
  2. 02Odoo のバージョンに応じた実装方法を選択
  3. 03Figma のデザインを Odoo テンプレートに変換
  4. 04オフライン対応を追加しウェブサイトの性能を確認

MCP 対応

README では、Figma デザイン変換と Chrome DevTools のスタイル抽出に MCP を利用すると記載されています。

インストール

コマンドをコピーしてターミナルで実行します。

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

インストール後、Claude Code で/odoo-frontendを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

scripts/
bootstrap_mapper.py
create_theme.py
devtools_extractor.py
figma_converter.py
pwa_generator.py
theme_mirror_generator.py
version_detector.py
templates/theme_module/
__manifest__.py.template

詳細

Odoo フロントエンド開発スキル v6.0

概要

このスキルでは、高度な Odoo フロントエンド開発機能を利用できます。

  • 🎨 /create-theme コマンド:本番環境ですぐに使える完全なテーマモジュールを、すべてのファイルとともに生成します
  • 🔧 テーマ機能の有効化:theme.utils モデルと、テンプレートを設定するための _theme_xxx_post_copy() を使用します
  • 📋 動的ページの完全なリファレンス:XML ID を含む、11 種類すべてのヘッダー、9 種類のフッター、ショップ、商品、ブログのテンプレート
  • 🎯 デザインワークフロー:Figma → Odoo テンプレートの照合 → 設定 → 改善の手法
  • テーマ開発:$o-website-values-palettes の完全なリファレンス、色の意味、テーマミラーモデル
  • 自動検出:Odoo のバージョンを自動検出し、対応する Bootstrap のバージョンを特定します
  • publicWidget フレームワーク:ウェブサイトビルダーの editableMode 処理を含む、包括的なパターン
  • プログレッシブウェブアプリ:サービスワーカー、オフラインサポート、プッシュ通知
  • 最新の JavaScript:TypeScript、ES2020 以降、ウェブコンポーネント、Owl v1/v2 のパターン
  • テストフレームワーク:Jest、Cypress、ビジュアルリグレッションテスト
  • パフォーマンスの最適化:Core Web Vitals、リソースヒント、クリティカル CSS
  • アクセシビリティ:WCAG 2.1 AA 準拠、ARIA パターン、キーボード操作
  • リアルタイム機能:WebSocket、Server-Sent Events、リアルタイム共同作業
  • MCP 統合:Figma デザインの変換と Chrome DevTools によるスタイル抽出
  • テーマのスキャフォールディング:適切な構成を備えた完全なテーマモジュールの生成
  • バージョン対応:Odoo 14~19 の違い(Bootstrap 4/5、Owl v1/v2、スニペット構造)に対応します
  • DevOps 対応:CI/CD パイプライン、Vite ビルド、自動テスト

🎨 /create-theme コマンド

クイックスタート:本番環境ですぐに使える完全な Odoo テーマモジュールを生成します。

使用方法
# Interactive mode (recommended)
/create-theme

# Quick mode with arguments
/create-theme <theme_name> <project_path>

# Full arguments
/create-theme <theme_name> <project_path> --version=17 --colors="#207AB7,#FB9F54,#F6F4F0,#FFFFFF,#191A19" --font="IBM Plex Sans"
作成されるもの
theme_<name>/
├── __init__.py
├── __manifest__.py
├── security/
│   └── ir.model.access.csv
├── data/
│   ├── assets.xml
│   ├── menu.xml
│   └── pages/                        # Individual page files (BEST PRACTICE!)
│       ├── home_page.xml            # Homepage (inherits website.homepage)
│       ├── aboutus_page.xml         # About Us page
│       ├── contactus_page.xml       # Contact (inherits website.contactus)
│       └── services_page.xml        # Services page
├── views/
│   ├── layout/
│   │   ├── header.xml               # Header customization (OPTIONAL)
│   │   ├── footer.xml               # Footer customization (OPTIONAL)
│   │   └── templates.xml            # Base layout templates
│   └── snippets/
│       └── custom_snippets.xml      # Custom snippet definitions
└── static/src/
    ├── scss/
    │   ├── primary_variables.scss   # Theme variables + fonts
    │   ├── bootstrap_overridden.scss # Bootstrap overrides (OPTIONAL)
    │   └── theme.scss               # Additional custom styles
    ├── js/
    │   ├── theme.js                 # publicWidget implementations
    │   └── snippets_options.js      # Snippet options (if needed)
    └── img/
💡 シンプルな方法(推奨)

ほとんどの場合、カスタム XML を使わずに $o-website-values-palettes で設定できます。

  • 'header-template':'default' | 'hamburger' | 'vertical' | 'sidebar'
  • 'footer-template':'default' | 'centered' | 'minimalist' | 'links' | 'descriptive'

カスタム header.xml/footer.xml を使用する場合:

  • テンプレートでは対応できない完全なカスタムレイアウトがデザイン上必要な場合
  • テンプレートが提供する要素以外に HTML 要素を追加する必要がある場合
カラーシステム(o-color-1 ~ o-color-5)
変数意味デフォルト
o-color-1ブランドのメインカラー#207AB7
o-color-2セカンダリカラー/アクセントカラー#FB9F54
o-color-3明るい背景色#F6F4F0
o-color-4白/本文のベースカラー#FFFFFF
o-color-5暗いテキスト/見出し#191A19
バージョンサポート
  • Odoo 14-15:Bootstrap 4.5.0、シンプルなスニペット
  • Odoo 16-17:Bootstrap 5.1.3、最新のアセットバンドル
  • Odoo 18-19:Bootstrap 5.1.3、スニペットグループが必要
スクリプトを直接実行
python scripts/create_theme.py <theme_name> <output_path> --version=17 --colors="#207AB7,#FB9F54,#F6F4F0,#FFFFFF,#191A19"

重要なルール

  1. odoo/ または odoo/addons/ ディレクトリ内の Odoo コアは絶対に編集しないでください
  2. インライン JS/CSS は使用せず、個別の .js ファイルと .scss ファイルを作成してください
  3. JS モジュール:/** @odoo-module **/ アノテーションを使用してください
  4. ウェブサイトテーマ:Owl やバニラ JS ではなく、publicWidget フレームワークのみを使用してください
  5. Bootstrap:Odoo 16 以降では v5.1.3 のクラスを使用してください(Tailwind は使用しないでください)
  6. モジュール名:snake_case 規則を使用してください
  7. 翻訳:静的なラベルは、実行時のラッパーではなく、定義時に _t() を使って JS の配列/定数で囲んでください。静的な XML 文字列は自動的に翻訳されます。

自動検出ワークフロー

ステップ 1:Odoo のバージョンを検出する

フロントエンドのタスクを開始するときは、まず Odoo のバージョンを検出してください。

# Use the version detector script
python scripts/version_detector.py <module_path>

バージョン検出機能は、次の処理を行います。

  1. __manifest__.py または __openerp__.py を探します
  2. バージョンフィールドを解析します
  3. 見つからない場合は、親ディレクトリ(例:odoo17/)から検出します
  4. 次の情報を返します。
    • Odoo のバージョン(例:"17.0")
    • Bootstrap のバージョン(例:"5.1.3")
    • Owl のバージョン(例:"2.x" または None)
    • モジュールの種類(テーマ、ウェブサイト、カスタム)
ステップ 2:Bootstrap バージョンのマッピング

Bootstrap バージョンの自動選択:

  • Odoo 14-15:Bootstrap 4.5.0
  • Odoo 16-19:Bootstrap 5.1.3
ステップ 3:Owl バージョンの検出

JavaScript フレームワークの選択:

  • Odoo 14-15:Owl の実験版、または jQuery のみ
  • Odoo 16-17:Owl v1
  • Odoo 18-19:Owl v2(破壊的変更あり)
ステップ 4:スニペット構造の検出

スニペット登録方法:

  • Odoo 14-17:グループなしでスニペットを挿入
  • Odoo 18-19:スニペットグループが必須(グループを含む snippet_structure)

テーマ変数の完全リファレンス

このセクションでは、Odoo テーマで使用される3つの主要な SCSS 変数システムについて、完全なドキュメントを提供します。


📚 1. $o-theme-font-configs - Google Fonts の設定

$o-theme-font-configs は、Google Fonts を自動的にインポートして、Odoo ウェブサイトのテーマで使用可能なフォントを定義します。

構造
$o-theme-font-configs: (
    '<Font Name>': (
        'family': (<CSS font-family list>),     // Required: Font family with fallbacks
        'url': '<Google Fonts parameter>',       // Required*: Google Fonts query param ONLY
        'properties': (                          // Optional: Per-context CSS overrides
            '<font-alias>': (
                '<website-value-key>': <value>,
            ),
        ),
    ),
);

⚠️ 重要:'url' キーに含めるのはクエリパラメーターのみであり、完全な URL ではありません。

// ✅ CORRECT - Only the font parameter
'url': 'Poppins:300,300i,400,400i,600,600i,700,700i'

// The system generates: https://fonts.googleapis.com/css?family=Poppins:...&display=swap

// ❌ WRONG - Do not include full URL
'url': 'https://fonts.googleapis.com/css?family=Poppins:300,400,700'
フォントウェイトの指定
// Format: FontName:weight1,weight1i,weight2,weight2i,...
//   - Number alone = normal style
//   - Number + 'i' = italic style

'url': 'Poppins:300,300i,400,400i,600,600i,700,700i'
//      Light  Light-i  Regular Regular-i SemiBold ...
複数の単語から成るフォント名
// Replace spaces with +
'url': 'Open+Sans:300,300i,400,400i,700,700i'
'url': 'Source+Sans+Pro:300,300i,400,400i,700,700i'
'url': 'DM+Serif+Display:400,400i'
フォントのエイリアス('properties' キー用)
エイリアス対応先用途
'base''font'本文テキスト
'headings''headings-font'すべての見出し(H1-H6)
'h2' - 'h6''h2-font' - 'h6-font'個別の見出し
'navbar''navbar-font'ナビゲーションメニュー
'buttons''buttons-font'ボタンのテキスト
'display-1' - 'display-4''display-1-font' - 'display-4-font'ディスプレイテキスト
完全な例
// ⚠️ STANDALONE definition - NO map-merge with core variables!
$o-theme-font-configs: (
    'Poppins': (
        'family': ('Poppins', sans-serif),
        'url': 'Poppins:300,300i,400,400i,500,500i,600,600i,700,700i',
        'properties': (
            'base': (
                'font-size-base': (15 / 16) * 1rem,
                'header-font-size': (15 / 16) * 1rem,
            ),
        ),
    ),
    'Playfair Display': (
        'family': ('Playfair Display', serif),
        'url': 'Playfair+Display:400,400i,700,700i',
    ),
    'Inter': (
        'family': ('Inter', sans-serif),
        'url': 'Inter:300,400,500,600,700',
    ),
);
アラビア語/RTL フォントのサポート
$o-theme-font-configs: (
    'IBM Plex Sans Arabic': (
        'family': ('IBM Plex Sans Arabic', sans-serif),
        'url': 'IBM+Plex+Sans+Arabic:100,200,300,400,500,600,700',
    ),
    'Cairo': (
        'family': ('Cairo', sans-serif),
        'url': 'Cairo:200,300,400,500,600,700,800,900',
    ),
    'Almarai': (
        'family': ('Almarai', sans-serif),
        'url': 'Almarai:300,400,700,800',
    ),
);

📚 2. $o-color-palettes - カラーシステム

$o-color-palettes は、5つの基本色とコンポーネントへの割り当てを含む、すべてのカラーパレットを定義します。

5つの基本色
色意味一般的な用途
o-color-1プライマリ/アクセントブランドカラー、ボタン、リンク、強調表示
o-color-2セカンダリ補色となるアクセント、セカンダリボタン
o-color-3明るい背景セクションの背景、カード、明るい領域
o-color-4白/最も明るい色メインコンテンツの背景(通常は #FFFFFF)
o-color-5暗色/テキスト暗い背景、テキストの色、フッター
視覚的な表現
┌─────────────────────────────────────────────────────────┐
│  o-color-4 (White Background)                           │
│  ┌─────────────────────────────────────────────────┐   │
│  │  o-color-3 (Light Section)                      │   │
│  │    Text: o-color-5 (Dark)                       │   │
│  │    Links: o-color-1 (Primary)                   │   │
│  │    Buttons: o-color-1 (Primary), o-color-2 (Sec)│   │
│  └─────────────────────────────────────────────────┘   │
│  ┌─────────────────────────────────────────────────┐   │
│  │  o-color-5 (Dark Section - Footer)              │   │
│  │    Text: o-color-4 (White)                      │   │
│  │    Links: o-color-3 (Light)                     │   │
│  └─────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────┘
カラーコンビネーション(o_cc1 - o_cc5)

カラーコンビネーションは、背景、テキスト、見出し、リンク、ボタンを自動設定するプリセットの配色です。

クラス背景一般的な用途
o_cc1o-color-4(白)メインコンテンツ領域
o_cc2o-color-3(明るい色)交互に配置するセクション
o_cc3o-color-2(セカンダリ)アクセントセクション
o_cc4o-color-1(プライマリ)行動喚起セクション
o_cc5o-color-5(暗色)フッター、暗いセクション
カラーパレットの構造
$o-color-palettes: map-merge($o-color-palettes, (
    'my-palette': (
        // Required: The 5 core colors
        'o-color-1': #09294F,      // Primary (Navy blue)
        'o-color-2': #FFA807,      // Secondary (Orange)
        'o-color-3': #F6F4F0,      // Light background
        'o-color-4': #FFFFFF,      // White
        'o-color-5': #1B212C,      // Dark

        // Component color assignments (1-5 → o_cc1-o_cc5)
        'menu': 1,                 // Menu uses o_cc1
        'footer': 5,               // Footer uses o_cc5
        'copyright': 5,            // Copyright uses o_cc5

        // Color combination overrides
        'o-cc1-text': 'o-color-5',
        'o-cc1-headings': 'o-color-5',
        'o-cc5-link': 'o-color-4',
        'o-cc5-btn-primary': 'o-color-2',
    ),
));
カラーコンビネーションのオーバーライド構文
// Override specific colors within a combination
'o-cc{n}-{property}': value

// Available properties:
'o-cc1-text': #333333,           // Text color
'o-cc1-headings': 'o-color-5',   // Headings color
'o-cc1-link': 'o-color-1',       // Link color
'o-cc1-btn-primary': 'o-color-1', // Primary button
'o-cc1-btn-secondary': 'o-color-2', // Secondary button

// Example for dark background (o_cc5)
'o-cc5-text': rgba(#fff, .8),    // Semi-transparent white
'o-cc5-headings': 'o-color-4',   // White headings
'o-cc5-link': 'o-color-4',       // White links
'o-cc5-btn-primary': 'o-color-2', // Orange buttons on dark
HTML での使用方法

<section class="o_cc o_cc1 pt32 pb32">
    <div class="container"><h2>Content</h2></div>
</section>


<section class="o_cc o_cc2 pt32 pb32">...</section>


<section class="o_cc o_cc4 pt32 pb32">...</section>


<section class="o_cc o_cc5 pt32 pb32">...</section>

📚 3. $o-website-values-palettes - 完全な設定(115以上のキー)

$o-website-values-palettes は、Bootstrap コンポーネント、タイポグラフィ、ボタン、入力欄、ヘッダー/フッターのテンプレートなどを制御するマスター設定変数です。

カテゴリ別クイックリファレンス
カテゴリキーの数説明
タイポグラフィとフォント13フォントファミリーの設定
フォントサイズ13基本サイズと見出しのサイズ
行の高さ11テキストの間隔
マージン22見出しと段落のマージン
ボタン17ボタンのスタイル
入力欄12フォームフィールドのスタイル
ヘッダー13ヘッダー/ナビゲーションの設定
フッター3フッターの設定
リンク1リンクの下線の動作
レイアウト3ページレイアウト
色とグラデーション5カラーパレットとグラデーション
Google Fonts2追加フォントの読み込み
合計115以上
3.1 タイポグラフィとフォント
KeyTypeDefaultDescription
'font'stringFirst in configサイト全体の基本フォント
'headings-font'stringInherits fontH1~H6 見出しのフォント
'navbar-font'stringInherits fontナビゲーションメニューのフォント
'buttons-font'stringInherits fontボタンテキストのフォント
'h2-font' - 'h6-font'stringInherits headings個別の見出しフォント
'display-1-font' - 'display-4-font'stringInherits headingsディスプレイテキストのフォント
3.2 フォントサイズ
KeyTypeDefaultDescription
'font-size-base'size1rem基本フォントサイズ(16px)
'small-font-size'size0.875rem小さなテキスト(14px)
'h1-font-size' - 'h6-font-size'sizeCalculated見出しのサイズ
'display-1-font-size' - 'display-6-font-size'size5rem-2.5remディスプレイサイズ
3.3 行の高さとマージン
KeyTypeDefaultDescription
'body-line-height'number1.5本文の行の高さ
'headings-line-height'number1.2すべての見出しの行の高さ
'h2-line-height' - 'h6-line-height'numberInherits個別の見出しの行の高さ
'paragraph-margin-top'size0段落の上マージン
'paragraph-margin-bottom'size16px段落の下マージン
'headings-margin-top'size0見出しの上マージン
'headings-margin-bottom'size0.5rem見出しの下マージン
'h2-margin-top' - 'h6-margin-top'sizeInherits個別のマージン
'h2-margin-bottom' - 'h6-margin-bottom'sizeInherits個別のマージン
3.4 ボタン(17 個のキー)
KeyTypeDefaultDescription
'btn-padding-y'sizeBootstrap垂直方向のパディング
'btn-padding-x'sizeBootstrap水平方向のパディング
'btn-font-size'sizeBootstrapフォントサイズ
'btn-padding-y-sm' / 'btn-padding-x-sm'sizeBootstrap小さいボタンのパディング
'btn-padding-y-lg' / 'btn-padding-x-lg'sizeBootstrap大きいボタンのパディング
'btn-font-size-sm' / 'btn-font-size-lg'sizeBootstrapサイズバリエーション
'btn-border-width'sizeBootstrap枠線の太さ
'btn-border-radius'sizeBootstrap角の丸み
'btn-border-radius-sm' / 'btn-border-radius-lg'sizeBootstrapサイズ別の角の丸み
'btn-primary-outline'booleanfalseプライマリをアウトライン表示
'btn-secondary-outline'booleanfalseセカンダリをアウトライン表示
'btn-primary-flat'booleanfalseフラットなプライマリスタイル
'btn-secondary-flat'booleanfalseフラットなセカンダリスタイル
'btn-ripple'booleanfalseMaterial Design のリップル効果
3.5 入力欄とフォーム(12 個のキー)
KeyTypeDefaultDescription
'input-padding-y' / 'input-padding-x'sizeBootstrap入力欄のパディング
'input-font-size'sizeBootstrap入力欄のフォントサイズ
'input-padding-y-sm' / 'input-padding-x-sm'sizeBootstrap小さい入力欄のパディング
'input-padding-y-lg' / 'input-padding-x-lg'sizeBootstrap大きい入力欄のパディング
'input-border-width'sizeBootstrap枠線の太さ
'input-border-radius'sizeBootstrap角の丸み
'input-border-radius-sm' / 'input-border-radius-lg'sizeBootstrapサイズ別の角の丸み
3.6 ヘッダーとナビゲーション(13 個のキー)
キー型値デフォルト説明
'header-template'string以下を参照'default'ヘッダーのレイアウト
'header-links-style'string以下を参照'default'ナビゲーションリンクのスタイル
'header-font-size'sizeCSS lengthBootstrapヘッダーテキストのサイズ
'logo-height'sizeCSS lengthNavbar heightロゴの高さ
'fixed-logo-height'sizeCSS lengthSmaller固定表示時のロゴの高さ
'hamburger-position'string'left'/'center'/'right''left'デスクトップでの位置
'hamburger-position-mobile'string'left'/'center'/'right''left'モバイルでの位置
'menu-border-width'sizeCSS lengthnullメニューの枠線
'menu-border-radius'sizeCSS lengthnullメニューの角
'menu-box-shadow'CSSshadow/nonenullメニューの影
'sidebar-width'sizeCSS length18.75remサイドバーの幅

ヘッダーテンプレートのオプション:

  • 'default' - 標準的な横型ナビゲーションバー
  • 'hamburger' - ハンバーガーメニュー(折りたたみ式)
  • 'vertical' - 縦型サイドバーナビゲーション
  • 'sidebar' - サイドバー全体のレイアウト

ヘッダーリンクスタイルのオプション:

  • 'default' - 標準的なリンク
  • 'fill' - ホバー時に背景を塗りつぶす
  • 'outline' - ホバー時に枠線を表示
  • 'pills' - 丸みのあるピル型リンク
  • 'block' - ブロックスタイルのリンク
  • 'border-bottom' - ホバー時に下線の枠線を表示
3.7 フッター(3 個のキー)
キー型値デフォルト説明
'footer-template'stringテンプレート名'default'フッターのレイアウト
'footer-effect'string以下を参照nullアニメーション効果
'footer-scrolltop'booleantrue/falsefalseページトップへ戻るボタン

フッターテンプレートのオプション:

  • 'default' - 標準的なフッター
  • 'centered' - 中央揃え
  • 'minimalist' - すっきりとした最小限のデザイン
  • 'links' - リンクを多く配置
  • 'descriptive' - 詳細な説明

フッターの効果:

  • null - 効果なし(静的)
  • 'slideout_slide_hover' - ホバー時にスライドして表示
  • 'slideout_shadow' - スクロール時に影を表示
3.8 リンク(1 個のキー)
キー型値デフォルト説明
'link-underline'string'never'/'hover'/'always''hover'下線の動作
3.9 レイアウト(3 個のキー)
キー型値デフォルト説明
'layout'string'full'/'boxed''full'ページのレイアウト
'body-image'URL画像パスnull背景画像
'body-image-type'string'image'/'pattern''image'背景の種類
3.10 色とグラデーション(5 個のキー)
キー型デフォルト説明
'color-palettes-name'stringnull使用中のカラーパレット名
'menu-gradient'CSS gradientnullメニューの背景グラデーション
'menu-secondary-gradient'CSS gradientnullセカンダリメニューのグラデーション
'footer-gradient'CSS gradientnullフッターの背景グラデーション
'copyright-gradient'CSS gradientnull著作権表示のグラデーション
3.11 Google Fonts(2 個のキー)
キー型デフォルト説明
'google-fonts'listnull読み込む追加の Google Fonts
'google-local-fonts'mapnullローカルでホストされるフォント

完全な例 - モダンな企業テーマ
$o-website-values-palettes: (
    (
        // === REQUIRED ===
        'color-palettes-name': 'my-corporate-palette',

        // === TYPOGRAPHY ===
        'font': 'Inter',
        'headings-font': 'Inter',
        'navbar-font': 'Inter',
        'buttons-font': 'Inter',
        'font-size-base': 1rem,
        'headings-line-height': 1.3,
        'body-line-height': 1.6,

        // === HEADER (NO custom header.xml needed!) ===
        'header-template': 'default',      // or 'hamburger', 'vertical', 'sidebar'
        'header-links-style': 'default',   // or 'pills', 'fill', 'border-bottom'
        'logo-height': 48px,
        'fixed-logo-height': 36px,

        // === BUTTONS ===
        'btn-padding-y': 0.75rem,
        'btn-padding-x': 1.5rem,
        'btn-padding-y-lg': 1rem,
        'btn-padding-x-lg': 2rem,
        'btn-border-radius': 8px,
        'btn-border-radius-lg': 12px,
        'btn-ripple': true,

        // === INPUTS ===
        'input-padding-y': 0.75rem,
        'input-padding-x': 1rem,
        'input-border-radius': 8px,

        // === FOOTER (NO custom footer.xml needed!) ===
        'footer-template': 'default',      // or 'centered', 'minimalist', 'links'
        'footer-scrolltop': true,

        // === LINKS ===
        'link-underline': 'hover',         // or 'never', 'always'

        // === LAYOUT ===
        'layout': 'full',                  // or 'boxed'
    ),
);
⚠️ 重要:Odoo テーマの SCSS 読み込み順序

テーマの SCSS ファイルは、Odoo コア変数が定義される前に読み込まれます!

マニフェストのアセットバンドルで prepend を使用すると、SCSS は Odoo コアの primary_variables.scss より前に実行されます。

LOAD ORDER:
1. YOUR theme's primary_variables.scss (via prepend)  ← FIRST
2. Odoo core primary_variables.scss                    ← SECOND
3. Other SCSS files

⛔ 重大な制限事項:

  • コア変数に対して map-merge() は使用できません(この時点ではまだ存在しません!)
  • $o-color-palettes、$o-theme-color-palettes、$o-theme-font-configs は、テーマの読み込み時にはすべて未定義です

❌ 誤り(「Undefined variable」エラーが発生します):

$o-color-palettes: map-merge($o-color-palettes, (...));         // ERROR!
$o-theme-font-configs: map-merge($o-theme-font-configs, (...)); // ERROR!

✅ 正しい(単独で定義します):

// Standalone font config (no map-merge!)
$o-theme-font-configs: (
    'Poppins': (
        'family': ('Poppins', sans-serif),
        'url': 'Poppins:300,300i,400,400i,500,500i,600,600i,700,700i',
    ),
);

// Reference existing palette by name
$o-website-values-palettes: (
    (
        'color-palettes-name': 'default-1',  // Use existing palette name!
        'font': 'Poppins',
        // ...other values
    ),
);

❌ 誤り:テーマで Google Fonts に ir.asset レコードを使用しないでください。URL の形式が不正になります!


テーマページ作成の標準

個別ページファイルのパターン(推奨)

単一の pages.xml ではなく、個別のページファイルを作成します。

theme_name/
├── data/
│   ├── home.xml          # Homepage template + page
│   ├── about.xml         # About page template + page
│   ├── contact.xml       # Contact page (inherits website.contactus)
│   ├── services.xml      # Services page template + page
│   └── menu.xml          # Menu configuration
└── views/
    └── templates.xml     # Shared templates and layout
ホームページ(website.homepage を継承)

<template id="view_home" inherit_id="website.homepage" name="Home">
    <xpath expr="//div[@id='wrap']" position="replace">
        <div id="wrap" class="oe_structure">
            
        </div>
    </xpath>
</template>
お問い合わせページ(website.contactus を継承)

<template id="view_contact" inherit_id="website.contactus" name="Contact">
    <xpath expr="//h1" position="replace">
        <h1>Get in Touch</h1>
    </xpath>
</template>
カスタムページ(theme.website.page)

<template id="view_about" name="About">
    <t t-call="website.layout">
        <div id="wrap" class="oe_structure">
            <section class="s_title pt96 pb48">
                <div class="container">
                    <h1>About Us</h1>
                </div>
            </section>
        </div>
    </t>
</template>

<record id="page_about" model="theme.website.page">
    <field name="view_id" ref="view_about"/>
    <field name="is_published" eval="True"/>
    <field name="url">/about</field>
    <field name="name">About</field>
</record>

テーマミラーモデルのアーキテクチャ

テーマがページにインストールされる仕組み
Theme Module XML
    ↓
theme.ir.ui.view (Template View)
    ↓
theme.website.page (Template Page)
    ↓ (Theme Installation)
    ↓
ir.ui.view (Actual View with website_id)
    ↓
website.page (Actual Page with website_id)
重要なポイント
  • テーマモジュールには theme.* モデル(テンプレート)が含まれます
  • インストール時に、これらは website_id を持つ実際のモデルに変換されます
  • 各ウェブサイトには独立したコピーが作成されるため、テーマを再利用できます
  • website_id の割り当てはビューの作成時に行われます

テーマのスキャフォールディングコマンド

コマンド:完全なテーマモジュールをスキャフォールディングする

トリガー:ユーザーが「テーマを作成」「テーマをスキャフォールディング」「テーマモジュールを生成」と依頼した場合

ワークフロー:

  1. コンテキストを検出

    # Determine current Odoo version
    cd <project_path>
    python -c "import sys; sys.path.insert(0, 'helpers'); from version_detector import detect_version; print(detect_version('.'))"
    
  2. モジュール構造を作成

    theme_<name>/
    ├── __init__.py
    ├── __manifest__.py
    ├── security/
    │   └── ir.model.access.csv
    ├── data/
    │   ├── assets.xml
    │   ├── menu.xml
    │   └── pages/
    │       ├── home_page.xml
    │       └── aboutus_page.xml
    ├── views/
    │   ├── layout/
    │   │   ├── header.xml          # Header customization (OPTIONAL)
    │   │   ├── footer.xml          # Footer customization (OPTIONAL)
    │   │   └── templates.xml       # Base layout templates
    │   └── snippets/
    │       └── custom_snippets.xml # Custom snippet definitions
    ├── static/
    │   └── src/
    │       ├── scss/
    │       │   ├── primary_variables.scss
    │       │   ├── bootstrap_overridden.scss
    │       │   └── theme.scss
    │       ├── js/
    │       │   ├── theme.js
    │       │   └── snippets_options.js
    │       └── img/
    └── README.md
    
  3. __manifest__.py を生成

    {
        'name': 'Theme <Name>',
        'version': '<odoo_version>.1.0.0',
        'category': 'Website/Theme',
        'author': 'TaqaTechno',
        'website': 'https://www.taqatechno.com/',
        'support': '[email protected]',
        'license': 'LGPL-3',
        'depends': ['website'],
        'data': [
            'security/ir.model.access.csv',
            'views/layout/templates.xml',
            'views/layout/header.xml',
            'views/layout/footer.xml',
            'views/snippets/custom_snippets.xml',
            'data/menu.xml',
            'data/pages/home_page.xml',
            'data/pages/aboutus_page.xml',
            'data/pages/contactus_page.xml',
        ],
        'assets': {
            'web._assets_primary_variables': [
                ('prepend', 'theme_<name>/static/src/scss/primary_variables.scss'),
            ],
            'web._assets_frontend_helpers': [
                'theme_<name>/static/src/scss/bootstrap_overridden.scss',
            ],
            'web.assets_frontend': [
                'theme_<name>/static/src/scss/theme.scss',
                'theme_<name>/static/src/js/theme.js',
            ],
            'website.assets_wysiwyg': [
                'theme_<name>/static/src/js/snippets_options.js',
            ],
        },
        'installable': True,
        'auto_install': False,
        'application': False,
    }
    
  4. primary_variables.scss を生成(完全版 v5.0)

    // ===================================================================
    // Theme: <Name>
    // Generated by TAQAT Techno /create-theme command v5.0
    // ===================================================================
    //
    // ⚠️ IMPORTANT: This file is PREPENDED before Odoo core variables!
    // DO NOT use map-merge() with core variables - they don't exist yet!
    // ===================================================================
    
    // === Font Configuration (STANDALONE - no map-merge!) ===
    $o-theme-font-configs: (
        'Inter': (
            'family': ('Inter', sans-serif),
            'url': 'Inter:300,300i,400,400i,500,500i,600,600i,700,700i',
        ),
    );
    
    // === Website Values Palette (MASTER CONFIGURATION) ===
    // NO bootstrap_overridden.scss needed - configure everything here!
    $o-website-values-palettes: (
        (
            // Reference existing palette (avoids map-merge issues)
            'color-palettes-name': 'default-1',
    
            // === Typography ===
            'font': 'Inter',
            'headings-font': 'Inter',
            'navbar-font': 'Inter',
            'buttons-font': 'Inter',
    
            // === Header (NO custom header.xml needed!) ===
            // Options: 'default' | 'hamburger' | 'vertical' | 'sidebar'
            'header-template': 'default',
            'header-links-style': 'default',
            'logo-height': 3rem,
            'fixed-logo-height': 2rem,
    
            // === Buttons ===
            'btn-padding-y': 0.45rem,
            'btn-padding-x': 1.35rem,
            'btn-border-radius': 0.25rem,
    
            // === Inputs ===
            'input-padding-y': 0.45rem,
            'input-border-radius': 0.25rem,
    
            // === Footer (NO custom footer.xml needed!) ===
            // Options: 'default' | 'centered' | 'minimalist' | 'links' | 'descriptive'
            'footer-template': 'default',
            'footer-scrolltop': true,
    
            // === Links & Layout ===
            'link-underline': 'hover',
            'layout': 'full',
        )
    );
    
  5. 初期テンプレートを生成

    <?xml version="1.0" encoding="utf-8"?>
    <odoo>
        
        <template id="layout" inherit_id="website.layout">
            <xpath expr="//head" position="inside">
                <meta name="theme-name" content="<Name>"/>
            </xpath>
        </template>
    </odoo>
    
コマンド:スニペットを作成(バージョン対応)

トリガー:ユーザーが「スニペットを作成」「カスタムスニペットを追加」と依頼した場合

ワークフロー:

  1. Odoo のバージョンを検出(登録方法を決定します)

  2. Odoo 17 以前の場合:

    
    <template id="s_<snippet_name>" name="<Snippet Name>">
        <section class="s_<snippet_name>" data-name="<Snippet Name>">
            <div class="container">
                
            </div>
        </section>
    </template>
    
    
    <template id="s_<snippet_name>_insert" inherit_id="website.snippets">
        <xpath expr="//div[@id='snippet_effect']//t[@t-snippet][last()]" position="after">
            <t t-snippet="theme_<name>.s_<snippet_name>"
               string="<Snippet Name>"
               t-thumbnail="/theme_<name>/static/img/snippets/<snippet_name>.svg"/>
        </xpath>
    </template>
    
  3. Odoo 18/19 の場合:

    
    <template id="s_<snippet_name>" name="<Snippet Name>">
        <section class="s_<snippet_name>" data-name="<Snippet Name>">
            <div class="container">
                
            </div>
        </section>
    </template>
    
    
    <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>
    
    
    <template id="s_<snippet_name>_insert" inherit_id="website.snippets">
        <xpath expr="//div[@id='snippet_structure']/*[1]" position="before">
            <t t-snippet="theme_<name>.s_<snippet_name>"
               string="<Snippet Name>"
               group="custom"
               t-thumbnail="/theme_<name>/static/img/snippets/<snippet_name>.svg"/>
        </xpath>
    </template>
    
  4. スニペットのオプションを生成(必要な場合)

    <template id="s_<snippet_name>_options" inherit_id="website.snippet_options">
        <xpath expr="." position="inside">
            <div data-selector=".s_<snippet_name>">
                
                <we-select string="Layout">
                    <we-button data-select-class="">Default</we-button>
                    <we-button data-select-class="s_<snippet_name>_alt">Alternate</we-button>
                </we-select>
    
                
                <we-row string="Items">
                    <we-button data-add-item="true"
                               data-no-preview="true"
                               class="o_we_bg_brand_primary">Add Item</we-button>
                </we-row>
    
                
                <we-colorpicker string="Background Color"
                                data-css-property="background-color"/>
            </div>
        </xpath>
    </template>
    
  5. オプション用 JavaScript を生成(動的な動作が必要な場合)

    /** @odoo-module **/
    
    import options from "@web_editor/js/editor/snippets.options";
    
    options.registry.<SnippetName> = options.Class.extend({
        /**
         * Add item to snippet
         */
        addItem: function(previewMode, widgetValue, params) {
            let $lastItem = this.$target.find('.snippet-item').last();
            if ($lastItem.length) {
                $lastItem.clone().appendTo(this.$target.find('.snippet-container'));
            }
        },
    });
    

    マニフェストのアセットに含めます:

    'assets': {
        'website.assets_wysiwyg': [
            'theme_<name>/static/src/js/snippets_options.js',
        ],
    }
    

Figma 統合ワークフロー

コマンド:Figma デザインを Odoo テーマに変換

トリガー:ユーザーが Figma の URL を提示するか、「Figma を Odoo に変換」「Figma デザインをインポート」と依頼した場合

前提条件:Figma MCP が利用可能であること

ワークフロー:

  1. バージョン固有のプロンプトで Figma MCP を使用

    Convert this Figma design to HTML for Odoo <version> website theme with these requirements:
    - Use Bootstrap v<bootstrap_version> classes (not Tailwind)
    - Apply proper Odoo theme structure with sections and containers
    - Include responsive classes (col-sm-*, col-md-*, col-lg-*)
    - Use semantic HTML5 elements (header, nav, main, section, article)
    - Add data attributes for Odoo website builder compatibility
    - Map colors to CSS custom properties for theme variables
    - Include accessibility attributes (aria-labels, alt text)
    
  2. カラーパレットを抽出

    Extract the color palette from this Figma design and map to CSS custom properties using the o-color-1 through o-color-5 naming convention for Odoo themes.
    
  3. HTML を QWeb テンプレートに変換

    • <template> タグで囲みます
    • t-name 属性を追加します
    • Odoo エディター用のデータ属性を追加します
    • セクションベースの構造に変換します
  4. Figma の色から SCSS を生成

    python scripts/figma_converter.py <figma_url> <output_dir> <odoo_version>
    
  5. Figma コンポーネントからスニペットを作成

    • 変換した HTML をスニペットのコンテンツとして使用します
    • 適切な Odoo 構造を追加します
    • バージョン(17 と 18/19)に応じてスニペットを登録します
    • インタラクティブな場合はオプションを生成します
Figma MCP プロンプトのリファレンス

基本的な HTML + Bootstrap:

Generate this Figma selection as HTML with Bootstrap v<version> classes instead of React and Tailwind. Use Odoo-compatible structure with proper semantic HTML5 elements.

色の抽出:

Extract colors as CSS custom properties for Odoo theme

タイポグラフィの抽出:

Convert this Figma text styling to Bootstrap v<version> typography classes and CSS custom properties. Map font families to Google Fonts configuration for Odoo theme integration.

レイアウトの抽出:

Convert this Figma layout to Bootstrap v<version> grid system and utility classes. Use proper container, row, col-* structure with responsive breakpoints.

Chrome DevTools 統合ワークフロー

コマンド:ライブ Web サイトからスタイルを抽出する

トリガー:ユーザーが Web サイトの URL を提示する、または「Web サイトから抽出」「サイトからスタイルをコピー」と依頼する

前提条件:Chrome DevTools MCP が利用可能であること

ワークフロー:

  1. DevTools MCP に接続する

    • Chrome DevTools MCP で URL を開く
    • 抽出する要素を選択する
  2. 計算済みスタイルを抽出する

    python scripts/devtools_extractor.py <url> <output_dir>
    
  3. Odoo SCSS に変換する

    • CSS プロパティを SCSS 変数に対応付ける
    • 色を Odoo のテーマ変数(o-color-*)に変換する
    • 適用できる場合は Bootstrap 変数を使用する
  4. Bootstrap ユーティリティに対応付ける

    • 同等の Bootstrap クラスを特定する
    • 可能な場合はカスタム CSS をユーティリティに置き換える
    • クラスベースのアプローチに関する提案を生成する
  5. DOM を QWeb に変換する

    • HTML 構造を抽出する
    • Odoo 固有の属性を追加する
    • 適切なテンプレート構造で囲む
    • Bootstrap グリッド構造を追加する
  6. テーマコード全体を生成する

    • 抽出したスタイルを含む SCSS ファイル
    • 構造を含む QWeb テンプレート
    • Bootstrap クラスの提案
    • スニペット登録用 XML

JavaScript 開発

Public Widget パターン(publicWidget — テーマでは必須)

重要:Web サイトのテーマでは、必ず publicWidget フレームワークを使用してください。Owl やバニラ JavaScript は使用しないでください。

用途:Web サイトのインタラクション、テーマの機能、フォーム処理、アニメーション

editableMode を含む完全な構造:

/** @odoo-module **/

import publicWidget from "@web/legacy/js/public/public_widget";

publicWidget.registry.MyWidget = publicWidget.Widget.extend({
    selector: '.my-selector',
    disabledInEditableMode: false,  // Allow in website builder

    events: {
        'click .button': '_onClick',
        'change input': '_onChange',
        'submit form': '_onSubmit',
    },

    /**
     * Lifecycle: Called when widget starts
     * CRITICAL: Check editableMode for website builder compatibility
     */
    start: function () {
        // IMPORTANT: Only run animations/interactions in read mode
        if (!this.editableMode) {
            this._initializeAnimation();
            this._bindExternalEvents();
        }
        return this._super.apply(this, arguments);
    },

    /**
     * Initialize animations (only in read mode)
     */
    _initializeAnimation: function () {
        // Animation code that should NOT run in edit mode
        this.$el.addClass('animated');
    },

    /**
     * Bind events outside the widget element
     */
    _bindExternalEvents: function () {
        $(window).on('scroll.myWidget', this._onScroll.bind(this));
        $(window).on('resize.myWidget', this._onResize.bind(this));
    },

    /**
     * Click handler
     */
    _onClick: function (ev) {
        ev.preventDefault();
        // Don't run in edit mode
        if (this.editableMode) return;
        // Handler logic
    },

    /**
     * Scroll handler
     */
    _onScroll: function () {
        // Throttle scroll events for performance
    },

    /**
     * CRITICAL: Clean up event listeners to prevent memory leaks
     */
    destroy: function () {
        $(window).off('.myWidget');  // Remove namespaced events
        this._super.apply(this, arguments);
    },
});

export default publicWidget.registry.MyWidget;

publicWidget の重要なポイント:

  1. アニメーションやインタラクションを実行する前に、必ず this.editableMode を確認する
  2. Web サイトビルダーでウィジェットを動作させるには、disabledInEditableMode: false を使用する
  3. destroy() メソッドでイベントリスナーを必ずクリーンアップする
  4. Web サイトのテーマでは、Owl やバニラ JavaScript は絶対に使用せず、publicWidget のみを使用する
  5. 簡単にクリーンアップできるよう、名前空間付きイベント(.myWidget)を使用する

マニフェストに含める:

'assets': {
    'web.assets_frontend': [
        'module_name/static/src/js/my_widget.js',
    ],
}
Owl コンポーネントパターン(モダン、リアクティブ)

用途:複雑なインタラクティブ UI、リアクティブな状態管理、コンポーネントの合成

Odoo 17(Owl v1):

/** @odoo-module **/

import { Component, useState } from "@odoo/owl";
import { registry } from "@web/core/registry";

class MyComponent extends Component {
    setup() {
        this.state = useState({
            items: [],
            loading: false,
        });
    }

    async willStart() {
        await this.loadData();
    }

    async loadData() {
        this.state.loading = true;
        // Fetch data via RPC
        const data = await this.rpc('/my/route');
        this.state.items = data.items;
        this.state.loading = false;
    }

    onItemClick(item) {
        console.log('Clicked:', item);
    }
}

MyComponent.template = "module_name.MyComponentTemplate";

registry.category("public_components").add("MyComponent", MyComponent);

export default MyComponent;

Odoo 18/19(Owl v2 — 相違点に注意):

/** @odoo-module **/

import { Component, useState } from "@odoo/owl";

class MyComponent extends Component {
    static template = "module_name.MyComponentTemplate";
    static props = {
        // Define props with validation
        title: { type: String, optional: true },
        items: { type: Array },
    };

    setup() {
        this.state = useState({
            selectedId: null,
        });
    }

    // Owl v2 uses static template property
}

export default MyComponent;

XML テンプレート:

<template id="MyComponentTemplate" name="My Component">
    <div class="my-component">
        <h3 t-if="props.title"><t t-esc="props.title"/></h3>
        <ul>
            <li t-foreach="props.items" t-as="item" t-key="item.id">
                <t t-esc="item.name"/>
            </li>
        </ul>
    </div>
</template>
翻訳(_t)のベストプラクティス

重要なルール:JavaScript の配列や定数内にある静的ラベルには、実行時のラッパーメソッドではなく、定義時に _t() を使用してください。XML テンプレート内の静的文字列は、Odoo によって自動的に翻訳されます。

理由

  • XML テンプレート(.xml ファイル)内の静的文字列は、Odoo の翻訳システムによって自動的に抽出されます
  • 静的文字列に対してテンプレート内で _t() を使うと、翻訳エントリが重複し、不要なオーバーヘッドが発生します
  • Odoo の PO 抽出ツールが文字列を検出できるように、JavaScript では定義時に文字列を _t() で囲む必要があります
  • 実行時のラッパーメソッド(translateLabel(key) など)は機能しません。抽出ツールが文字列を検出できないためです

正しい例 — 静的ラベルは定義時に _t() で囲む:

/** @odoo-module **/

import { _t } from "@web/core/l10n/translation";

// ✅ CORRECT: Wrap each string with _t() at definition time
const MONTHS = [
    {value: 1, short: _t("Jan"), full: _t("January")},
    {value: 2, short: _t("Feb"), full: _t("February")},
    {value: 3, short: _t("Mar"), full: _t("March")},
    {value: 4, short: _t("Apr"), full: _t("April")},
    {value: 5, short: _t("May"), full: _t("May")},
    {value: 6, short: _t("Jun"), full: _t("June")},
    {value: 7, short: _t("Jul"), full: _t("July")},
    {value: 8, short: _t("Aug"), full: _t("August")},
    {value: 9, short: _t("Sep"), full: _t("September")},
    {value: 10, short: _t("Oct"), full: _t("October")},
    {value: 11, short: _t("Nov"), full: _t("November")},
    {value: 12, short: _t("Dec"), full: _t("December")},
];

// ✅ CORRECT: Status labels wrapped at definition
const STATUS_LABELS = {
    draft: _t("Draft"),
    pending: _t("Pending"),
    approved: _t("Approved"),
    rejected: _t("Rejected"),
};

export class DatePicker extends Component {
    setup() {
        this.months = MONTHS;  // Already translated at definition
    }

    get displayLabel() {
        const month = this.months.find(m => m.value === this.selectedMonth);
        // No translateLabel() needed - values are already translated
        return `${month?.short || ""} ${this.selectedYear}`;
    }
}

誤った例 — 実行時のラッパーメソッドは機能しない:

// ❌ WRONG: Strings without _t() at definition will NOT be translated
const MONTHS = [
    {value: 1, label: "Jan", full: "January"},  // NOT FOUND by PO extractor!
    {value: 2, label: "Feb", full: "February"},
];

// ❌ WRONG: This pattern does NOT work for translation
translateLabel(key) {
    return _t(key);  // key is a variable, not a literal - PO extractor can't find it!
}

正しい例 — あらかじめ翻訳された値を使うテンプレート:


<t t-foreach="getAvailableMonths()" t-as="month" t-key="month.value">
    <a href="#" t-on-click.prevent="() => this.selectMonth(month.value)">
        
        <t t-esc="month.full"/>
    </a>
</t>

正しい例 — 静的な XML 文字列はラッパー不要:


<span>No data available</span>
<a href="#">Live</a>
<button type="submit">Submit</button>

_t() を定義時に使用する場合:

  1. ✅ JavaScript 配列内の静的ラベル(月名、曜日名、ステータスラベル)
  2. ✅ JavaScript オブジェクトや定数内の静的ラベル
  3. ✅ 定数として定義されたエラーメッセージ
  4. ✅ JavaScript からユーザーに表示される文字列リテラル

_t() を使用しない場合:

  1. ❌ XML テンプレート内に直接記述された静的テキスト(自動翻訳される)
  2. ❌ translateLabel(key) のような実行時のラッパーメソッド(機能しません!)
  3. ❌ 実行時に _t() に渡される動的変数
  4. ❌ .xml ファイル内のハードコードされた文字列

覚えておいてください:JavaScript で定義した文字列をユーザーに表示する場合は、使用する場所ではなく、定義する場所で _t() で囲んでください。

SCSS と Bootstrap のカスタマイズ

プライマリ変数のオーバーライド

ファイル:static/src/scss/primary_variables.scss

目的:Odoo に組み込まれているスタイル変数(色、フォント、間隔)をオーバーライドする

含める場所:web._assets_primary_variables バンドル

構造:

// ============================================================
// Theme Primary Variables
// Override Odoo defaults with !default to allow further overriding
// ============================================================

// Color Palette
$o-color-1: #3498db !default;  // Primary
$o-color-2: #2ecc71 !default;  // Success
$o-color-3: #e74c3c !default;  // Danger
$o-color-4: #f39c12 !default;  // Warning
$o-color-5: #9b59b6 !default;  // Info

// Website Values Palette Configuration
$o-website-values-palettes: (
    (
        // Color Configuration
        'color-palettes-name': 'my-theme',

        // Typography
        'font': 'Inter',
        'headings-font': 'Inter',
        'navbar-font': 'Inter',
        'buttons-font': 'Inter',
        'headings-line-height': 1.2,

        // Header & Navigation
        'header-template': 'default',  // or 'centered', 'boxed'
        'header-font-size': 1rem,
        'logo-height': 3rem,
        'fixed-logo-height': 2rem,
        'hamburger-position': 'right',  // Mobile menu

        // Buttons
        'btn-padding-y': 0.45rem,
        'btn-padding-x': 1.35rem,
        'btn-padding-y-sm': 0.3rem,
        'btn-padding-x-sm': 0.9rem,
        'btn-padding-y-lg': 0.6rem,
        'btn-padding-x-lg': 1.8rem,
        'btn-border-radius': 0.25rem,
        'btn-border-width': 1px,
        'btn-font-size': 1rem,

        // Input Fields
        'input-padding-y': 0.45rem,
        'input-border-radius': 0.25rem,

        // Colors (map to palette)
        'color-1': $o-color-1,
        'color-2': $o-color-2,
        'color-3': $o-color-3,
        'color-4': $o-color-4,
        'color-5': $o-color-5,
    )
);

// Google Fonts Configuration (STANDALONE - no map-merge!)
$o-theme-font-configs: (
    'Inter': (
        'family': ('Inter', sans-serif),
        'url': 'Inter:wght@300;400;500;600;700',
    ),
);

⚠️ テーマでは次のパターンを絶対に使用しないでください:

// ❌ WRONG - Will cause "Undefined variable" errors
$o-color-palettes: map-merge($o-color-palettes, (...));
$o-theme-color-palettes: map-merge($o-theme-color-palettes, (...));
$o-theme-font-configs: map-merge($o-theme-font-configs, (...));
Bootstrap のオーバーライド

ファイル:static/src/scss/bootstrap_overridden.scss

目的:Odoo で公開されていない Bootstrap の変数をオーバーライドします

追加先:web._assets_frontend_helpers バンドル

構成:

// ============================================================
// Bootstrap Variable Overrides
// Only override Bootstrap vars not available in Odoo variables
// ============================================================

@import "~bootstrap/scss/functions";
@import "~bootstrap/scss/variables";

// Spacing
$spacer: 1rem !default;

// Grid breakpoints
$grid-breakpoints: (
    xs: 0,
    sm: 576px,
    md: 768px,
    lg: 992px,
    xl: 1200px,
    xxl: 1400px
) !default;

// Container max widths
$container-max-widths: (
    sm: 540px,
    md: 720px,
    lg: 960px,
    xl: 1140px,
    xxl: 1320px
) !default;

// Typography
$font-size-base: 1rem !default;
$line-height-base: 1.5 !default;

// Border radius
$border-radius: 0.25rem !default;
$border-radius-sm: 0.125rem !default;
$border-radius-lg: 0.5rem !default;

// Shadows
$box-shadow-sm: 0 .125rem .25rem rgba(0, 0, 0, .075) !default;
$box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15) !default;
$box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, .175) !default;

バージョン固有の考慮事項

Odoo 17 固有の事項
  1. Owl v1

    • @odoo/owl から useState を使用します
    • レガシーなインポートパス
    • コンポーネントのテンプレートを個別のプロパティとして指定します
  2. スニペットの構造

    • グループを使わずにシンプルに挿入します
    • //div[@id='snippet_effect'] をターゲットとする XPath を使用します
  3. パブリックウィジェット

    • @web/legacy/js/public/public_widget からインポートします
    • jQuery を全面的に利用できます
Odoo 18/19 固有の事項
  1. Owl v2

    • 静的なテンプレートプロパティ
    • Props の検証が必須です
    • 新しいリアクティブフック
    • v1 からの破壊的変更があります
  2. スニペットグループ

    • snippet-group 属性が必須です
    • カテゴリーごとに整理します
    • //div[@id='snippet_structure'] をターゲットとする XPath を使用します
  3. Bootstrap 5.1.3

    • すべて同じ Bootstrap バージョンを使用します
    • ユーティリティクラスが統一されています
  4. ウェブサイトビルダーの機能強化

    • エディターでテーマを閲覧できます
    • UI からカスタムフォントをアップロードできます
    • スニペットのオプションが強化されています
Bootstrap 4 から 5 への移行(Odoo 14/15 → 16+)

テーマを移行する場合:

  1. クラスの置き換え:

    python scripts/bootstrap_mapper.py <old_classes>
    

    よくある置き換え:

    • ml-* → ms-*(margin-left → margin-start)
    • mr-* → me-*(margin-right → margin-end)
    • pl-* → ps-*(padding-left → padding-start)
    • pr-* → pe-*(padding-right → padding-end)
    • text-left → text-start
    • text-right → text-end
    • float-left → float-start
    • float-right → float-end
    • form-group → mb-3
    • custom-select → form-select
    • close → btn-close
    • badge-* → bg-*(例:badge-primary → bg-primary)
    • font-weight-bold → fw-bold
    • sr-only → visually-hidden
    • no-gutters → g-0
  2. 削除されたクラス(代替手段を探してください):

    • form-inline - グリッドまたはフレックスのユーティリティを使用します
    • jumbotron - ユーティリティを使って再現します
    • media - フレックスのユーティリティとともに d-flex を使用します
  3. jQuery の削除:

    • Bootstrap 5 ではバニラ JavaScript を使用します
    • ウィジェットの初期化を調整する必要がある場合があります

フロントエンドのパフォーマンスに関するベストプラクティス

  1. アセットのバンドル

    • 常に Odoo のアセットバンドルを使用します
    • 外部ファイルを個別にリンクしないでください
    • 本番環境で有効になる圧縮を活用します
  2. 遅延読み込み

    • 画像に loading="lazy" を使用します
    • 自動リサイズには Odoo の /website/image ルートを活用します
    • 動画を遅延読み込みします(Odoo 18 以降では組み込みでサポートされています)
  3. スニペットの最適化

    • サーバー呼び出しをまとめます(すべてのデータに対して RPC を 1 回実行します)
    • 効率的なレンダリングには QWeb を使用します
    • DOM 操作を最小限に抑えます
  4. CSS の最適化

    • カスタム CSS の代わりに Bootstrap のユーティリティを使用します
    • HTML 構造を簡潔に保ちます
    • バンドルから未使用の CSS/JS を削除します
  5. キャッシュ

    • store=True を指定した計算フィールドを活用します
    • コストの高い計算結果をキャッシュします
    • 負荷の高い処理はサーバー側で行います
  6. SEO に関する考慮事項

    • 重要なコンテンツはサーバー側でレンダリングします
    • Owl は機能拡張に使用し、主要なコンテンツには使用しません
    • 高速な読み込み時間を確保します

セキュリティとアクセス制御

モデルレベルのアクセス制御

必ず security/ir.model.access.csv を作成します:

id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_model_user,model.user,model_model_name,base.group_user,1,1,1,0
access_model_public,model.public,model_model_name,,1,0,0,0
レコードレベルのルール

細かな制御を行うには security/rules.xml を作成します:

<record id="model_rule" model="ir.rule">
    <field name="name">Model: User own records</field>
    <field name="model_id" ref="model_model_name"/>
    <field name="domain_force">[('user_id', '=', user.id)]</field>
    <field name="groups" eval="[(4, ref('base.group_user'))]"/>
</record>
コントローラーのセキュリティ
from odoo import http

class MyController(http.Controller):
    @http.route('/public/route', auth='public')  # No login required
    def public_route(self):
        pass

    @http.route('/user/route', auth='user')  # Login required
    def user_route(self):
        pass
安全なデータ公開
# Use sudo() carefully
records = request.env['model'].sudo().search([('published', '=', True)])

# Check access rights
if not request.env['model'].check_access_rights('read', raise_exception=False):
    return request.redirect('/access-denied')

よくあるワークフロー

Figma から完全なテーマを作成する
  1. ユーザーから Figma の URL を受け取ります
  2. 対象となる Odoo のバージョンを検出します
  3. Figma MCP を使用してデザインを抽出します
  4. テーマモジュールの構造を生成します
  5. 色を SCSS 変数に変換します
  6. コンポーネントをスニペットに変換します
  7. スニペットのオプションを作成します
  8. Odoo ウェブサイトビルダーでテストします
既存のテーマを拡張する
  1. 現在のテーマとバージョンを検出します
  2. 既存の構造を分析します
  3. 継承するモジュールを作成します
  4. 特定のテンプレートをオーバーライドします
  5. カスタムスニペットを追加します
  6. SCSS 変数を拡張します
バージョン間でのテーマの移行
  1. 移行元と移行先のバージョンを検出します
  2. Bootstrap クラスを変換します(4→5 の場合)
  3. スニペットの構造を更新します(17→18/19 の場合)
  4. JavaScript を更新します(Owl v1→v2 の場合)
  5. すべての機能をテストします
  6. 移行レポートを生成します

テストと検証

テーマのインストールテスト
# Install theme
python -m odoo -c conf/<config>.conf -d <db> -i theme_<name> --stop-after-init

# Check for errors in log
grep ERROR odoo.log
スニペット登録テスト
  1. テーマをインストールします
  2. Website → Edit に移動します
  3. スニペットパネルを確認します
  4. スニペットが正しいカテゴリに表示されることを確認します
  5. ドラッグ&ドロップをテストします
  6. スニペットのオプションをテストします
ブラウザテスト
  1. 複数のブラウザ(Chrome、Firefox、Safari、Edge)でテストします
  2. レスポンシブブレークポイント(sm、md、lg、xl、xxl)をテストします
  3. アクセシビリティ(ARIA ラベル、キーボードナビゲーション)を検証します
  4. パフォーマンスを確認します(Lighthouse、Page Speed)

トラブルシューティング

⚠️ SCSS のコンパイルエラー(重大)

問題:「Undefined variable: $o-color-palettes」または「$o-theme-font-configs」

原因: テーマの SCSS で、コアの Odoo 変数とともに map-merge() を使用しています。テーマファイルはコア変数が定義される前に読み込まれます。

解決策:

  1. コア変数を使った map-merge() の呼び出しをすべて削除します
  2. $o-theme-font-configs を単独で定義します(マージしません)
  3. 既存のパレットを参照するには 'color-palettes-name': 'default-1' を使用します
  4. 上記の「重大:SCSS の読み込み順」セクションを参照してください

問題:CSS のルール数が 0 と表示される/スタイルが読み込まれない

原因: 未定義の変数による SCSS コンパイルのサイレントな失敗です。

診断:

// In browser console
document.styleSheets[0].cssRules.length  // Should be > 0
// If 0, SCSS compilation failed silently

解決策:

  1. ブラウザーコンソールで「Style compilation failed」エラーを確認します
  2. 未定義の変数エラーを修正します(上記を参照)
  3. アセットキャッシュをクリアします:
# Via Odoo shell
>>> self.env['ir.attachment'].search([('url', 'like', '/web/assets/')]).unlink()
>>> self.env.cr.commit()

問題:Google Fonts の URL が不正な形式になる

原因: 外部フォント URL に ir.asset レコードを使用すると、URL が重複します。

解決策: ir.asset の XML レコードではなく、SCSS で $o-theme-font-configs を使用します。

スニペットが表示されない

原因:

  • XPath が正しくない(バージョンの不一致)
  • group 属性がない(Odoo 18/19)
  • テンプレートが見つからない
  • XML の構文エラー

解決策:

  1. Odoo のバージョンを確認し、適切な挿入方法を使用します
  2. テンプレート ID が存在することを確認します
  3. XML の構文を確認します
  4. ログでエラーを確認します
スタイルが適用されない

原因:

  • SCSS が正しいバンドルに含まれていない
  • 変数のオーバーライドに !default が使われていない
  • CSS の詳細度の問題
  • アセットがコンパイルされていない
  • SCSS のコンパイルがサイレントに失敗している(コンソールを確認してください!)

解決策:

  1. マニフェストのアセットバンドルを確認します
  2. ブラウザーキャッシュをクリアします
  3. アセットの再生成を確認します:odoo-bin --update theme_<name>
  4. ブラウザーの DevTools で計算済みスタイルを調べます
  5. まずコンソールで SCSS エラーを確認してください!
JavaScript が動作しない

原因:

  • Odoo のバージョンに対してインポートパスが正しくない
  • ウィジェットが登録されていない
  • jQuery の競合(Bootstrap 5)
  • Owl のバージョンが一致していない

解決策:

  1. Odoo のバージョンに対応したインポートパスを確認します
  2. ウィジェット/コンポーネントの登録を確認します
  3. コンソールでエラーを確認します
  4. 正しい Owl のバージョンを使用していることを確認します
ヘッダー/フッターのテンプレートで XPath エラーが発生する

原因: //header//nav のような複雑な XPath 式が、Odoo の実際の HTML 構造に一致しない場合があります。

解決策: シンプルな XPath 式を使用します:


<xpath expr="//header" position="attributes">


<xpath expr="//header//nav" position="attributes">

エキスパート向けのヒント

  1. 必ず最初にバージョンを検出する

    • Odoo のバージョンを決めつけないでください
    • コードを生成する前に確認してください
  2. Bootstrap ユーティリティを優先する

    • カスタム CSS を減らすと、メンテナンスが容易になります
    • パフォーマンスが向上します
    • エディターとの互換性が高まります
  3. SEO にはサーバーサイドレンダリングを使用する

    • コンテンツをクライアント側の JS に依存させないでください
    • Owl は機能強化のみに使用します
  4. 編集モードでテストする

    • Website Builder がコードを処理できることを確認します
    • スニペットのオプションを十分にテストします
  5. Odoo のパターンに従う

    • コアテーマ(odoo/addons/theme_*)を調べます
    • 既存の規約に合わせます
    • 同じクラス接頭辞(s_、o_)を使用します
  6. ドキュメント

    • 複雑な SCSS にコメントを付けます
    • スニペットのオプションを文書化します
    • テーマに README を用意します

プログレッシブウェブアプリ(PWA)の実装

コマンド:Odoo Website 用 PWA のセットアップ

トリガー:ユーザーが「PWA を作成」「オフラインサポートを追加」「プッシュ通知を有効化」と依頼した場合

ワークフロー:

  1. サービスワーカーを生成

    // static/src/service-worker.js
    const CACHE_NAME = 'odoo-pwa-v1';
    const urlsToCache = ['/', '/web/static/lib/owl/owl.js'];
    
    self.addEventListener('install', event => {
      event.waitUntil(
        caches.open(CACHE_NAME)
          .then(cache => cache.addAll(urlsToCache))
      );
    });
    
    self.addEventListener('fetch', event => {
      event.respondWith(
        caches.match(event.request)
          .then(response => response || fetch(event.request))
      );
    });
    
  2. Web アプリマニフェストを作成

    {
      "name": "Odoo PWA",
      "short_name": "OdooPWA",
      "start_url": "/",
      "display": "standalone",
      "background_color": "#875A7B",
      "theme_color": "#875A7B",
      "icons": [...]
    }
    
  3. サービスワーカーを登録

    /** @odoo-module **/
    import publicWidget from "@web/legacy/js/public/public_widget";
    
    publicWidget.registry.ServiceWorkerRegistration = publicWidget.Widget.extend({
      selector: 'body',
      start: function() {
        if ('serviceWorker' in navigator) {
          navigator.serviceWorker.register('/service-worker.js');
        }
        return this._super(...arguments);
      }
    });
    

モダンな JavaScript と TypeScript

コマンド:TypeScript のセットアップ

トリガー:ユーザーが「TypeScript を使用」「型安全性を追加」「TypeScript を設定」と依頼した場合

ワークフロー:

  1. tsconfig.json を生成

    {
      "compilerOptions": {
        "target": "ES2020",
        "module": "ES2020",
        "lib": ["ES2020", "DOM"],
        "strict": true,
        "paths": {
          "@web/*": ["./addons/web/static/src/*"],
          "@owl/*": ["./addons/web/static/lib/owl/*"]
        }
      }
    }
    
  2. 型付き Owl コンポーネントを作成

    /** @odoo-module **/
    import { Component, useState } from "@odoo/owl";
    
    interface ProductProps {
      id: number;
      name: string;
      price: number;
    }
    
    export class ProductComponent extends Component<ProductProps> {
      static template = xml`...`;
    
      setup() {
        this.state = useState({ loading: false });
      }
    }
    
ES2020 以降のパターン

モダンな JavaScript の機能を使用する:

// Optional chaining
const userName = this.props.user?.profile?.name;

// Nullish coalescing
const displayName = userName ?? 'Guest User';

// Dynamic imports
const module = await import('./HeavyFeatureComponent');

// Private class fields
class SecureWidget {
  #apiKey = null;
  #generateKey() {
    return crypto.randomUUID();
  }
}

テストインフラストラクチャ

コマンド:テストのセットアップ

トリガー:ユーザーが「テストを追加」「Jest をセットアップ」「Cypress を設定」と依頼した場合

ワークフロー:

  1. Jest の設定

    // jest.config.js
    module.exports = {
      testEnvironment: 'jsdom',
      moduleNameMapper: {
        '^@web/(.*)$': '<rootDir>/addons/web/static/src/$1',
      },
      collectCoverageFrom: ['static/src/**/*.{js,jsx}']
    };
    
  2. Cypress E2E テスト

    // cypress/e2e/ecommerce.cy.js
    describe('E-commerce Flow', () => {
      it('should complete purchase', () => {
        cy.visit('/shop');
        cy.get('[data-product-id="1"]').click();
        cy.get('#add_to_cart').click();
        cy.get('.btn-primary').contains('Checkout').click();
      });
    });
    
  3. ビジュアルリグレッションテスト

    // backstop.config.js
    module.exports = {
      scenarios: [{
        label: 'Homepage',
        url: 'http://localhost:8069',
        misMatchThreshold: 0.1
      }]
    };
    

パフォーマンスの最適化

コマンド:Core Web Vitals の最適化

トリガー:ユーザーが「パフォーマンスを改善」「LCP/FID/CLS を最適化」「ウェブサイトを高速化」と依頼した場合

ワークフロー:

  1. 最大コンテンツ描画(LCP)を最適化

    // Preload critical resources
    const link = document.createElement('link');
    link.rel = 'preload';
    link.as = 'image';
    link.href = '/web/image/website/1/logo';
    document.head.appendChild(link);
    
  2. 初回入力遅延(FID)を短縮

    // Break up long tasks
    function optimizeLongTask(items) {
      const chunks = [];
      for (let i = 0; i < items.length; i += 100) {
        chunks.push(items.slice(i, i + 100));
      }
      chunks.forEach(chunk => {
        requestIdleCallback(() => processChunk(chunk));
      });
    }
    
  3. 累積レイアウトシフト(CLS)を最小化

    /* Reserve space for dynamic content */
    .product-image-container {
      aspect-ratio: 1/1;
      contain: layout;
    }
    

アクセシビリティ(WCAG 準拠)

コマンド:アクセシビリティの実装

トリガー:ユーザーが「ARIA を追加」「アクセシビリティに対応」「WCAG に準拠」と依頼した場合

ワークフロー:

  1. ARIA の実装

    <form role="form" aria-label="Contact Form">
      <input type="email"
             aria-labelledby="email-label"
             aria-describedby="email-error"
             aria-required="true"
             aria-invalid="false"/>
      <span id="email-error" role="alert" aria-live="polite"></span>
    </form>
    
  2. キーボードナビゲーション

    class KeyboardNavigationManager {
      handleTab(event) {
        if (event.shiftKey) {
          // Backward navigation
        } else {
          // Forward navigation
        }
      }
      trapFocus() {
        // Trap focus within modal
      }
    }
    
  3. スクリーンリーダーのサポート

    class ScreenReaderAnnouncer {
      announce(message, priority = 'polite') {
        this.liveRegion.setAttribute('aria-live', priority);
        this.liveRegion.textContent = message;
      }
    }
    

リアルタイム機能

コマンド:WebSocket サポートの追加

トリガー:ユーザーが「リアルタイム機能を追加」「WebSocket を実装」「ライブ更新」と依頼した場合

ワークフロー:

  1. WebSocket マネージャー

    /** @odoo-module **/
    export class WebSocketManager {
      constructor(url) {
        this.socket = new WebSocket(url);
        this.setupEventHandlers();
      }
    
      setupEventHandlers() {
        this.socket.onmessage = (event) => {
          const data = JSON.parse(event.data);
          this.handleMessage(data);
        };
      }
    
      send(data) {
        if (this.socket.readyState === WebSocket.OPEN) {
          this.socket.send(JSON.stringify(data));
        }
      }
    }
    
  2. サーバー送信イベント(SSE)

    class SSEManager {
      constructor(url) {
        this.eventSource = new EventSource(url);
        this.eventSource.onmessage = (event) => {
          this.handleMessage(event.data);
        };
      }
    }
    

Web コンポーネント

コマンド:Web コンポーネントの作成

トリガー:ユーザーが「Web コンポーネントを作成」「カスタム要素」と依頼した場合

ワークフロー:

/** @odoo-module **/

export class OdooSearchInput extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
  }

  static get observedAttributes() {
    return ['placeholder', 'value'];
  }

  connectedCallback() {
    this.render();
  }

  render() {
    this.shadowRoot.innerHTML = `
      <style>
        :host { display: block; }
        input { width: 100%; padding: 0.5rem; }
      </style>
      <input type="text" placeholder="${this.getAttribute('placeholder')}"/>
    `;
  }
}

customElements.define('odoo-search-input', OdooSearchInput);

Odoo の高度な機能

ツアーマネージャー

コマンド:ガイド付きツアーを作成

/** @odoo-module **/
import tour from 'web_tour.tour';

tour.register('product_tour', {
  url: '/shop',
}, [{
  content: 'Click on any product',
  trigger: '.oe_product:first',
  position: 'bottom',
}]);
カスタムフィールドウィジェット
/** @odoo-module **/
import { registry } from "@web/core/registry";
import { Component } from "@odoo/owl";

export class ColorPickerField extends Component {
  static template = xml`
    <input type="color" t-att-value="props.value" t-on-change="onChange"/>
  `;

  onChange(event) {
    this.props.update(event.target.value);
  }
}

registry.category("fields").add("color_picker", ColorPickerField);
Systray コンポーネント
/** @odoo-module **/
import { registry } from "@web/core/registry";
import { Component, useState } from "@odoo/owl";

export class NotificationBell extends Component {
  static template = xml`...`;

  setup() {
    this.state = useState({ notifications: [] });
  }
}

registry.category("systray").add("NotificationBell", {
  Component: NotificationBell,
});

モダンなビルドツール

Vite の設定

コマンド:Vite ビルドのセットアップ

// vite.config.js
import { defineConfig } from 'vite';

export default defineConfig({
  base: '/web/static/',
  build: {
    rollupOptions: {
      input: {
        main: 'static/src/main.js',
        website: 'static/src/website.js'
      }
    }
  },
  server: {
    proxy: {
      '/web': 'http://localhost:8069'
    }
  }
});
CI/CD パイプライン
# .github/workflows/odoo-frontend.yml
name: Odoo Frontend CI/CD

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: Run tests
      run: |
        npm run test:unit
        npm run test:e2e
    - name: Build assets
      run: npm run build

リソース

テーマ機能の有効化システム(重要)

概要:theme.utils モデル

テーマを作成する際は、theme.utils パターンを実装する models/theme_xxx.py ファイルを必ず含めてください。これにより、テーマで以下の操作が可能になります。

  • ヘッダーテンプレートの有効化/無効化
  • フッターテンプレートの有効化/無効化
  • テーマのインストール時にオプション機能を有効化
  • テーマのデフォルトの外観を設定
必須のファイル構造
theme_yourtheme/
├── __init__.py                    # Must import models
├── __manifest__.py
├── models/
│   ├── __init__.py               # Must import theme_yourtheme
│   └── theme_yourtheme.py        # theme.utils implementation (REQUIRED!)
├── data/
├── views/
└── static/
models/init.py
from . import theme_yourtheme
models/theme_yourtheme.py(必須パターン)
from odoo import models

class ThemeYourTheme(models.AbstractModel):
    _inherit = 'theme.utils'

    def _theme_yourtheme_post_copy(self, mod):
        """
        Called automatically when theme is installed on a website.
        Use enable_view() and disable_view() to configure templates.

        IMPORTANT: Method name MUST be _theme_{technical_name}_post_copy
        where technical_name matches folder name (underscores, not hyphens).
        """
        # Enable desired header template (mutual exclusivity enforced)
        self.enable_view('website.template_header_sales_two')

        # Enable desired footer template (mutual exclusivity enforced)
        self.enable_view('website.template_footer_contact')

        # Enable optional features
        self.enable_view('website.option_header_brand_logo')

        # Disable unwanted defaults
        self.disable_view('website.header_visibility_standard')
        self.enable_view('website.header_visibility_fixed')
利用可能な主なメソッド
メソッド用途例
enable_view(xml_id)テンプレートを有効化self.enable_view('website.template_header_hamburger')
disable_view(xml_id)テンプレートを無効化self.disable_view('website.template_header_default')
enable_asset(xml_id)アセットバンドルを有効化self.enable_asset('website.theme_custom_assets')
disable_asset(xml_id)アセットバンドルを無効化self.disable_asset('website.theme_old_assets')
相互排他ルール

ヘッダー:有効にできるメインのヘッダーテンプレートは一度に 1 つだけです。ヘッダーで enable_view() を実行すると、Odoo がほかのヘッダーを自動的に無効化します。

フッター:有効にできるメインのフッターテンプレートは一度に 1 つだけです。同様に、自動的に処理されます。

ヘッダーオプション:配置のバリエーション、表示効果、コンポーネントは、有効なヘッダーと組み合わせることができます。

完全な例:モダンなビジネステーマ
from odoo import models

class ThemeModernBusiness(models.AbstractModel):
    _inherit = 'theme.utils'

    def _theme_modern_business_post_copy(self, mod):
        """Configure Modern Business theme defaults."""

        # === HEADER CONFIGURATION ===
        # Use hamburger header with centered mobile
        self.enable_view('website.template_header_hamburger')
        self.enable_view('website.template_header_hamburger_mobile_align_center')

        # Fixed header that disappears on scroll
        self.disable_view('website.header_visibility_standard')
        self.enable_view('website.header_visibility_disappears')

        # Show logo, not brand name
        self.enable_view('website.option_header_brand_logo')
        self.disable_view('website.option_header_brand_name')

        # === FOOTER CONFIGURATION ===
        # Use contact footer with call-to-action style
        self.enable_view('website.template_footer_contact')

        # Enable scroll-to-top button
        # (This is configured in $o-website-values-palettes, not via views)

        # === OPTIONAL FEATURES ===
        # Enable search in header
        self.enable_view('website.header_search_box')

        # Enable social links
        self.enable_view('website.header_social_links')

動的ページの完全なリファレンス

このセクションでは、Odoo で動的ページに利用できるすべてのテンプレートについて説明します。このリファレンスは、次のような場合にお使いください。

  1. Figma のデザインと比較して、最も近い Odoo テンプレートを見つける
  2. _theme_xxx_post_copy() を使ってテーマのデフォルトを設定する
  3. Odoo に標準で備わっている機能を把握する

📋 ヘッダーテンプレート(メイン 11 種+バリエーション)

すべてのヘッダーは website.layout を継承します。ウェブサイトごとに有効にできるメインヘッダーは 1 つだけです。

メインヘッダーテンプレート
XML ID名前説明最適な用途
website.template_header_defaultデフォルト標準的な横型ナビゲーションバーほとんどのウェブサイト
website.template_header_hamburgerハンバーガー折りたたみ式のハンバーガーメニューミニマルなデザイン
website.template_header_stretchストレッチ全幅に広がるナビゲーションバー横幅の広いレイアウト
website.template_header_vertical縦型縦型サイドバーナビゲーションアプリのようなサイト
website.template_header_search検索検索欄を目立たせたヘッダーE コマース、ディレクトリ
website.template_header_sales_oneセールス 1E コマース向けヘッダーオンラインストア
website.template_header_sales_twoセールス 2カテゴリ付きの E コマース向けヘッダー大規模なカタログ
website.template_header_sales_threeセールス 3E コマース向けの別バリエーション商品を重視するサイト
website.template_header_sales_fourセールス 4E コマース向けのバリエーションファッション、小売
website.template_header_sidebarサイドバーサイドバーを全面に配置したレイアウトダッシュボードサイト
website.template_header_boxedボックス型角丸のボックスコンテナーモダンなブランド
ヘッダー配置のバリエーション(各ヘッダー)

各ヘッダーには配置のバリエーションがあります。デフォルトヘッダーの例:

  • website.template_header_default - 左揃え(デフォルト)
  • website.template_header_default_align_center - 中央揃え
  • website.template_header_default_align_right - 右揃え

モバイルのバリエーション(_mobile_align_center または _mobile_align_right を末尾に追加):

  • website.template_header_hamburger_mobile_align_center
  • website.template_header_hamburger_mobile_align_right
ヘッダーの表示効果(相互排他的)
XML ID効果説明
website.header_visibility_standard標準常に表示され、ページとともにスクロールします
website.header_visibility_fixed固定スクロール時に画面上部に固定されます
website.header_visibility_disappears非表示下方向へのスクロールで隠れ、上方向へのスクロールで表示されます
website.header_visibility_fade_outフェードアウトスクロール時にフェードアウトします
ヘッダーコンポーネント(組み合わせ可能)
XML IDコンポーネントデフォルト
website.option_header_brand_logoロゴ画像を表示有効
website.option_header_brand_nameテキスト「My Website」を表示無効
website.header_call_to_actionCTA ボタン(お問い合わせ)有効
website.header_search_box検索バー有効
website.header_social_linksソーシャルメディアアイコン無効
website.header_text_elementカスタムテキスト要素有効
website.header_language_selector言語ドロップダウン有効
ヘッダーのビジュアルリファレンス
┌─────────────────────────────────────────────────────────────────┐
│ DEFAULT HEADER                                                   │
│ ┌────────┬────────────────────────────────┬───────────────────┐ │
│ │ LOGO   │  Home  About  Services  Blog   │  Search   CTA BTN │ │
│ └────────┴────────────────────────────────┴───────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│ HAMBURGER HEADER                                                 │
│ ┌────────┬───────────────────────────────┬────────────────────┐ │
│ │ ☰ MENU │         LOGO                  │   Search   CTA     │ │
│ └────────┴───────────────────────────────┴────────────────────┘ │
│                    ↓ (expanded menu)                             │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │  Home  │  About  │  Services  │  Blog  │  Contact           │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│ SALES TWO HEADER (E-commerce)                                    │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Top bar: Phone | Email | Currency | Language                 │ │
│ ├─────────────────────────────────────────────────────────────┤ │
│ │ LOGO        [    Search Bar    ]        Account  Cart 🛒    │ │
│ ├─────────────────────────────────────────────────────────────┤ │
│ │ Category 1  │  Category 2  │  Category 3  │  All Products   │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────────────┐
│ VERTICAL/SIDEBAR HEADER                                         │
│ ┌──────────────┬──────────────────────────────────────────────┐│
│ │              │                                               ││
│ │    LOGO      │                                               ││
│ │              │                                               ││
│ │  ─────────── │              PAGE CONTENT                     ││
│ │              │                                               ││
│ │   Home       │                                               ││
│ │   About      │                                               ││
│ │   Services   │                                               ││
│ │   Blog       │                                               ││
│ │   Contact    │                                               ││
│ │              │                                               ││
│ │  ─────────── │                                               ││
│ │  [Social]    │                                               ││
│ └──────────────┴──────────────────────────────────────────────┘│
└────────────────────────────────────────────────────────────────┘

📋 フッターテンプレート(9 種類)

すべてのフッターは website.layout を継承します。各ウェブサイトで有効にできるメインフッターは 1 つのみです。

XML ID名前説明最適な用途
website.footer_customデフォルトカスタマイズ可能なデフォルトのフッター一般用途
website.template_footer_descriptive詳細説明詳細な会社紹介企業サイト
website.template_footer_centered中央揃え中央揃えのシンプルなデザインランディングページ
website.template_footer_linksリンク複数列のリンク大規模サイト
website.template_footer_minimalistミニマリスト非常にシンプルなフッターすっきりとしたデザイン
website.template_footer_contactお問い合わせ連絡先情報を重視サービス業
website.template_footer_call_to_actionCTAニュースレター/アクションを重視マーケティングサイト
website.template_footer_headline見出し大きな見出しテキストブランドメッセージ
website.template_footer_slideoutスライド表示スクロール時にスライド表示モダン/トレンド重視
フッターオプション
XML IDオプション説明
website.footer_no_copyright著作権表示を削除著作権表示を非表示にします
website.footer_language_selector_inlineインライン言語表示言語セレクターのスタイル
フッターのビジュアルリファレンス
┌─────────────────────────────────────────────────────────────────┐
│ DEFAULT FOOTER                                                   │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │  LOGO                                                        │ │
│ │  Company description text here...                            │ │
│ │                                                              │ │
│ │  Links        Services      Contact                          │ │
│ │  · About      · Web Dev     123 Street                       │ │
│ │  · Blog       · Design      City, Country                    │ │
│ │  · Careers    · Support     +1 234 567 890                   │ │
│ │                                                              │ │
│ │  [Social Icons]                                              │ │
│ ├─────────────────────────────────────────────────────────────┤ │
│ │  © 2024 Company Name. All rights reserved.                   │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│ MINIMALIST FOOTER                                                │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │          © 2024 Company  ·  Privacy  ·  Terms               │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│ CONTACT FOOTER                                                   │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │  ┌──────────────┬───────────────┬────────────────────────┐  │ │
│ │  │  📍 Address  │  📞 Phone     │  ✉️ Email              │  │ │
│ │  │  123 Street  │  +1 234 567   │  [email protected]     │  │ │
│ │  │  City        │               │                        │  │ │
│ │  └──────────────┴───────────────┴────────────────────────┘  │ │
│ │                        [Social Icons]                        │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│ CALL-TO-ACTION FOOTER                                            │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │            Subscribe to our newsletter                       │ │
│ │  ┌─────────────────────────────────┬─────────────────────┐  │ │
│ │  │  Enter your email...            │  [Subscribe Now]    │  │ │
│ │  └─────────────────────────────────┴─────────────────────┘  │ │
│ │                                                              │ │
│ │  © 2024 Company  ·  [Social Icons]                          │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

📋 ショップページテンプレート(website_sale モジュール)
ショップのレイアウトオプション
XML ID機能デフォルト説明
website_sale.products_design_cardカードデザイン無効カード形式の商品項目
website_sale.products_design_thumbsサムネイル無効サムネイルレイアウト
website_sale.products_design_gridグリッドデザイン無効グリッドレイアウト
website_sale.products_thumb_4_34:3 比率無効4:3 の画像アスペクト比
website_sale.products_thumb_4_54:5 比率無効4:5 の画像アスペクト比
website_sale.products_thumb_2_32:3 比率無効2:3 の画像アスペクト比
website_sale.products_thumb_coverカバー表示有効画像をコンテナー全体に表示
ショップのカテゴリーとフィルター
XML ID機能デフォルト説明
website_sale.products_categories左サイドバーのカテゴリー無効左サイドバーにカテゴリーを表示
website_sale.products_categories_top上部のカテゴリー有効上部のナビゲーションにカテゴリーを表示
website_sale.products_attributes属性フィルター有効商品属性フィルター
website_sale.filter_products_price価格フィルター無効価格帯スライダー
website_sale.filter_products_tagsタグフィルター有効商品タグでフィルター
website_sale.search検索ボックス有効商品検索
website_sale.sort並べ替えドロップダウン有効商品を並べ替え
website_sale.add_grid_or_list_optionグリッド/リスト切り替え有効表示切り替えボタン
商品項目のオプション
XML ID機能デフォルト説明
website_sale.products_description説明無効一覧に説明を表示
website_sale.products_add_to_cartカートに追加無効一覧にカートに追加ボタンを表示
ショップのビジュアルリファレンス
┌─────────────────────────────────────────────────────────────────┐
│ SHOP PAGE (Categories Top - Default)                             │
├─────────────────────────────────────────────────────────────────┤
│  All  │  Category 1  │  Category 2  │  Category 3               │
├─────────────────────────────────────────────────────────────────┤
│  [Search...]          [Sort: Featured ▼]   [☷ Grid] [☰ List]   │
├─────────────────────────────────────────────────────────────────┤
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐        │
│  │  [IMG]   │  │  [IMG]   │  │  [IMG]   │  │  [IMG]   │        │
│  │          │  │          │  │          │  │          │        │
│  │ Product 1│  │ Product 2│  │ Product 3│  │ Product 4│        │
│  │ $99.00   │  │ $149.00  │  │ $79.00   │  │ $199.00  │        │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘        │
│                                                                  │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐        │
│  │  [IMG]   │  │  [IMG]   │  │  [IMG]   │  │  [IMG]   │        │
│  │ ...      │  │ ...      │  │ ...      │  │ ...      │        │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘        │
├─────────────────────────────────────────────────────────────────┤
│                    [1] [2] [3] ... [Next →]                      │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│ SHOP PAGE (Categories Left Sidebar)                              │
├────────────────┬────────────────────────────────────────────────┤
│ CATEGORIES     │  [Search...]     [Sort ▼]    [☷] [☰]          │
│ ─────────────  ├────────────────────────────────────────────────┤
│ □ All          │  ┌────────┐  ┌────────┐  ┌────────┐           │
│ ▸ Category 1   │  │ [IMG]  │  │ [IMG]  │  │ [IMG]  │           │
│   · Sub 1.1    │  │ Prod 1 │  │ Prod 2 │  │ Prod 3 │           │
│   · Sub 1.2    │  │ $99    │  │ $149   │  │ $79    │           │
│ ▸ Category 2   │  └────────┘  └────────┘  └────────┘           │
│ ▸ Category 3   │                                                │
│                │  ┌────────┐  ┌────────┐  ┌────────┐           │
│ PRICE          │  │ ...    │  │ ...    │  │ ...    │           │
│ ─────────────  │  └────────┘  └────────┘  └────────┘           │
│ $0 ─●────── $500│                                               │
│                │                                                │
│ TAGS           │                                                │
│ ─────────────  │                                                │
│ [New] [Sale]   │                                                │
└────────────────┴────────────────────────────────────────────────┘

📋 商品詳細ページテンプレート
XML ID機能デフォルト説明
website_sale.product_tags商品タグ有効商品タグを表示します
website_sale.product_commentレビュー無効ディスカッション/評価セクション
website_sale.product_custom_text規約ブロック有効利用規約
website_sale.product_share_buttons共有ボタン有効ソーシャル共有ボタン
website_sale.product_quantity数量選択有効数量入力フィールド
website_sale.product_buy_now今すぐ購入無効クイック購入ボタン
website_sale.product_variantsバリエーション一覧無効バリエーションのリスト表示
website_sale.alternative_products代替商品有効代替商品のカルーセル
website_sale.carousel_product_indicators_bottomカルーセル下部有効画像インジケーターを下部に表示
website_sale.carousel_product_indicators_leftカルーセル左側無効画像インジケーターを左側に表示
商品ページのビジュアルリファレンス
┌─────────────────────────────────────────────────────────────────┐
│ PRODUCT DETAIL PAGE                                              │
├─────────────────────────────────────────────────────────────────┤
│  Home > Category > Product Name                                  │
├───────────────────────────────┬─────────────────────────────────┤
│                               │                                  │
│  ┌─────────────────────────┐  │  Product Name                    │
│  │                         │  │  ★★★★☆ (24 reviews)             │
│  │      [MAIN IMAGE]       │  │                                  │
│  │                         │  │  $199.00                         │
│  │                         │  │  ̶$̶2̶4̶9̶.̶0̶0̶ -20%                     │
│  └─────────────────────────┘  │                                  │
│  [○] [○] [●] [○]              │  Color: [Blue ▼]                 │
│                               │  Size:  [M ▼]                    │
│  ┌──────┐ ┌──────┐ ┌──────┐  │                                  │
│  │thumb1│ │thumb2│ │thumb3│  │  Qty: [- 1 +]                    │
│  └──────┘ └──────┘ └──────┘  │                                  │
│                               │  [Add to Cart]  [Buy Now]        │
│                               │                                  │
│                               │  [♡ Wishlist] [🔗 Share]         │
│                               │                                  │
│                               │  Tags: [New] [Bestseller]        │
├───────────────────────────────┴─────────────────────────────────┤
│  DESCRIPTION  │  SPECIFICATIONS  │  REVIEWS (24)                 │
├─────────────────────────────────────────────────────────────────┤
│  Full product description text goes here...                      │
│                                                                  │
├─────────────────────────────────────────────────────────────────┤
│  ALTERNATIVE PRODUCTS                                            │
│  ┌────────┐  ┌────────┐  ┌────────┐  ┌────────┐                │
│  │ Alt 1  │  │ Alt 2  │  │ Alt 3  │  │ Alt 4  │                │
│  └────────┘  └────────┘  └────────┘  └────────┘                │
└─────────────────────────────────────────────────────────────────┘

📋 ブログページテンプレート(website_blog モジュール)
ブログ一覧のオプション
XML ID機能デフォルト説明
website_blog.opt_blog_cover_post最新記事バナー有効最新記事をバナーとして表示します
website_blog.opt_blog_cover_post_fullwidth_design全幅バナー有効バナーを全幅で表示します
website_blog.opt_blog_list_viewリスト表示無効投稿をグリッドではなくリストで表示します
website_blog.opt_blog_cards_designカードデザイン無効Bootstrap カードコンポーネント
website_blog.opt_blog_readable読みやすさ有効大きく読みやすいテキスト
website_blog.opt_blog_sidebar_showサイドバー無効ブログのサイドバーを表示します
ブログサイドバーのコンポーネント(有効時)
XML ID機能デフォルト説明
website_blog.opt_sidebar_blog_index_archivesアーカイブ有効日付別のアーカイブ
website_blog.opt_sidebar_blog_index_follow_usフォロー有効ソーシャルメディアへのリンク
website_blog.opt_sidebar_blog_index_tagsタグ有効タグクラウド
ブログ投稿ループの表示
XML ID機能デフォルト説明
website_blog.opt_posts_loop_show_coverカバー画像有効アイキャッチ画像
website_blog.opt_posts_loop_show_author作成者有効作成者の名前/アバター
website_blog.opt_posts_loop_show_stats統計情報無効コメント数/閲覧数
website_blog.opt_posts_loop_show_teaserティーザー有効プレビュー用テキストとタグ
ブログ投稿詳細のオプション
XML ID機能デフォルト説明
website_blog.opt_blog_post_readable読みやすさ有効大きく読みやすいテキスト
website_blog.opt_blog_post_sidebarサイドバー無効投稿のサイドバーを表示します
website_blog.opt_blog_post_regular_cover通常のカバー無効カバーの上にタイトルを表示します
website_blog.opt_blog_post_breadcrumbパンくずリスト有効ナビゲーション用パンくずリスト
website_blog.opt_blog_post_read_next次の記事を読む有効次の記事のバナー
website_blog.opt_blog_post_commentコメント無効コメントを有効にします
website_blog.opt_blog_post_select_to_tweet選択範囲をツイート無効ハイライトしたテキストをツイート
ブログのビジュアルリファレンス
┌─────────────────────────────────────────────────────────────────┐
│ BLOG LISTING (Grid View - Default)                               │
├─────────────────────────────────────────────────────────────────┤
│  ┌─────────────────────────────────────────────────────────────┐│
│  │             [FEATURED POST BANNER IMAGE]                     ││
│  │                                                              ││
│  │  Latest Article Title                                        ││
│  │  Short teaser text for the featured post...                  ││
│  │                        [Read More →]                         ││
│  └─────────────────────────────────────────────────────────────┘│
├─────────────────────────────────────────────────────────────────┤
│  All Posts  │  Category 1  │  Category 2  │  [Search...]        │
├─────────────────────────────────────────────────────────────────┤
│  ┌────────────────┐  ┌────────────────┐  ┌────────────────┐    │
│  │   [IMAGE]      │  │   [IMAGE]      │  │   [IMAGE]      │    │
│  │                │  │                │  │                │    │
│  │ Post Title 1   │  │ Post Title 2   │  │ Post Title 3   │    │
│  │ By Author      │  │ By Author      │  │ By Author      │    │
│  │ Jan 15, 2024   │  │ Jan 10, 2024   │  │ Jan 5, 2024    │    │
│  │                │  │                │  │                │    │
│  │ Teaser text... │  │ Teaser text... │  │ Teaser text... │    │
│  │ [Tag1] [Tag2]  │  │ [Tag1]         │  │ [Tag2] [Tag3]  │    │
│  └────────────────┘  └────────────────┘  └────────────────┘    │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│ BLOG POST DETAIL (Fullwidth Cover - Default)                     │
├─────────────────────────────────────────────────────────────────┤
│  ┌─────────────────────────────────────────────────────────────┐│
│  │                   [FULL WIDTH COVER IMAGE]                   ││
│  │                                                              ││
│  │           Article Title Goes Here                            ││
│  │           By Author Name  ·  January 15, 2024               ││
│  └─────────────────────────────────────────────────────────────┘│
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│     Full article content with readable typography.               │
│                                                                  │
│     Multiple paragraphs of content...                            │
│                                                                  │
│     [Share: Facebook | Twitter | LinkedIn]                       │
│                                                                  │
│     Tags: [Technology] [Tutorial] [Odoo]                         │
│                                                                  │
├─────────────────────────────────────────────────────────────────┤
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  READ NEXT: Next Article Title                               ││
│  │  [Banner Image]                                              ││
│  └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘

📋 カートとチェックアウトのテンプレート
XML ID機能デフォルト説明
website_sale.suggested_products_listおすすめ商品有効カート内の関連商品
website_sale.reduction_codeプロモーションコード有効クーポンコードの入力
website_sale.address_b2bB2B フィールド無効事業者住所のフィールド
website_sale.accept_terms_and_conditions利用規約のチェックボックス無効利用規約への同意を必須にします

デザインワークフロー:Figma から Odoo へ

手法

Figma のデザインを Odoo テーマに変換する際は、次のワークフローに従います。

┌─────────────────────────────────────────────────────────────────┐
│                    DESIGN WORKFLOW                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  1. ANALYZE DESIGN                                               │
│     ↓                                                            │
│     Extract: Colors, Fonts, Layout, Components                   │
│                                                                  │
│  2. COMPARE TO ODOO TEMPLATES                                    │
│     ↓                                                            │
│     Match: Header style, Footer style, Page layouts              │
│                                                                  │
│  3. CHOOSE CLOSEST TEMPLATE                                      │
│     ↓                                                            │
│     Select: Best matching header + footer + features             │
│                                                                  │
│  4. CONFIGURE VIA VARIABLES                                      │
│     ↓                                                            │
│     Set: $o-website-values-palettes configuration                │
│                                                                  │
│  5. ENHANCE WITH CUSTOM CSS                                      │
│     ↓                                                            │
│     Add: Only what templates can't provide                       │
│                                                                  │
│  6. CREATE CUSTOM SNIPPETS (if needed)                           │
│     ↓                                                            │
│     Build: Components not available in Odoo                      │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘
ステップ 1:Figma のデザインを分析する

次の主な要素を抽出します。

要素確認する内容対応先
プライマリーカラーブランドのメインカラー(ボタン、リンク、CTA)o-color-1
セカンダリーカラーアクセントカラー、セカンダリボタンo-color-2
明るい背景色セクションの背景、カードo-color-3
白/ベースカラーメインコンテンツの背景o-color-4
ダークカラー/テキストカラー見出し、フッター、暗色のセクションo-color-5
フォントファミリー本文、見出し$o-theme-font-configs
ヘッダースタイルナビゲーションのレイアウト'header-template'
フッタースタイルフッターのレイアウト'footer-template'
ステップ 2:ヘッダースタイルを合わせる
デザインの特徴使用するテンプレート
標準的な水平ナビゲーションtemplate_header_default
非表示メニュー(ハンバーガーアイコン)template_header_hamburger
全幅に広がるナビゲーションtemplate_header_stretch
縦型サイドバーナビゲーションtemplate_header_vertical
目立つ検索バーtemplate_header_search
カテゴリー付きの E コマースtemplate_header_sales_two
トップバーとメインナビゲーションtemplate_header_sales_one
コンテンツ付きサイドバーtemplate_header_sidebar
角丸/ボックス型コンテナーtemplate_header_boxed
ステップ 3:フッタースタイルを合わせる
デザインの特徴使用するテンプレート
ロゴ付きの複数列レイアウトfooter_custom(デフォルト)
詳細な会社情報template_footer_descriptive
中央揃えのミニマルなデザインtemplate_footer_centered
複数列のリンクのみtemplate_footer_links
著作権表示のみの超ミニマルなデザインtemplate_footer_minimalist
連絡先情報を重視template_footer_contact
ニュースレター/CTA を重視template_footer_call_to_action
大きな見出しテキストtemplate_footer_headline
モダンなスライドアウト効果template_footer_slideout
ステップ 4:テーマ設定を生成する

分析結果に基づいて、設定を作成します。

// primary_variables.scss - Generated from Figma Analysis

$o-theme-font-configs: (
    'Poppins': (
        'family': ('Poppins', sans-serif),
        'url': 'Poppins:300,400,500,600,700',
    ),
);

$o-website-values-palettes: (
    (
        'color-palettes-name': 'default-1',

        // Typography (from Figma)
        'font': 'Poppins',
        'headings-font': 'Poppins',

        // Header (matched to closest template)
        'header-template': 'hamburger',  // Figma shows hamburger menu
        'header-links-style': 'pills',   // Rounded pill-style links
        'logo-height': 48px,

        // Footer (matched to closest template)
        'footer-template': 'contact',    // Figma shows contact-focused footer
        'footer-scrolltop': true,

        // Buttons (from Figma measurements)
        'btn-padding-y': 0.75rem,
        'btn-padding-x': 1.5rem,
        'btn-border-radius': 8px,

        // Layout
        'link-underline': 'never',
    )
);
ステップ 5:theme.utils の実装を作成する
# models/theme_yourtheme.py

from odoo import models

class ThemeYourTheme(models.AbstractModel):
    _inherit = 'theme.utils'

    def _theme_yourtheme_post_copy(self, mod):
        # Enable matched header template
        self.enable_view('website.template_header_hamburger')
        self.enable_view('website.template_header_hamburger_mobile_align_center')

        # Enable matched footer template
        self.enable_view('website.template_footer_contact')

        # Enable desired header components
        self.enable_view('website.option_header_brand_logo')
        self.enable_view('website.header_search_box')

        # Configure visibility effect
        self.disable_view('website.header_visibility_standard')
        self.enable_view('website.header_visibility_fixed')
判断フローチャート:ヘッダーの選択
START: Analyze header design
    │
    ├─ Is navigation hidden by default?
    │   │
    │   ├─ YES → template_header_hamburger
    │   │
    │   └─ NO → Is there a vertical sidebar?
    │           │
    │           ├─ YES → Is it full-page sidebar?
    │           │   │
    │           │   ├─ YES → template_header_sidebar
    │           │   └─ NO → template_header_vertical
    │           │
    │           └─ NO → Is it e-commerce focused?
    │                   │
    │                   ├─ YES → Does it have category mega-menu?
    │                   │   │
    │                   │   ├─ YES → template_header_sales_two
    │                   │   └─ NO → template_header_sales_one
    │                   │
    │                   └─ NO → Is search prominent?
    │                           │
    │                           ├─ YES → template_header_search
    │                           │
    │                           └─ NO → Is it boxed/contained?
    │                                   │
    │                                   ├─ YES → template_header_boxed
    │                                   │
    │                                   └─ NO → Is it full-width?
    │                                           │
    │                                           ├─ YES → template_header_stretch
    │                                           └─ NO → template_header_default
判断フローチャート:フッターの選択
START: Analyze footer design
    │
    ├─ Is it minimal (just copyright)?
    │   │
    │   ├─ YES → template_footer_minimalist
    │   │
    │   └─ NO → Is it center-aligned?
    │           │
    │           ├─ YES → template_footer_centered
    │           │
    │           └─ NO → Is there a newsletter/CTA?
    │                   │
    │                   ├─ YES → template_footer_call_to_action
    │                   │
    │                   └─ NO → Is it contact info focused?
    │                           │
    │                           ├─ YES → template_footer_contact
    │                           │
    │                           └─ NO → Is it primarily links?
    │                                   │
    │                                   ├─ YES → template_footer_links
    │                                   │
    │                                   └─ NO → Has detailed description?
    │                                           │
    │                                           ├─ YES → template_footer_descriptive
    │                                           │
    │                                           └─ NO → footer_custom (default)

ウェブサイトスニペットの完全リファレンス

このセクションでは、Odoo で利用可能なすべてのスニペット、その仕組み、カスタムスニペットの作成方法を説明します。ウェブサイトのページの構築、カスタムスニペットの作成、またはウェブサイトビルダーの理解にお役立てください。


📚 スニペットアーキテクチャの概要

スニペットは、Odoo のウェブサイトページで再利用できる構成要素であり、ドラッグ&ドロップによるページ作成を可能にします。

┌─────────────────────────────────────────────────────────────────┐
│                     ODOO SNIPPET SYSTEM                          │
├─────────────────────────────────────────────────────────────────┤
│  ┌──────────────────┐    ┌──────────────────┐                   │
│  │  STATIC SNIPPETS │    │ DYNAMIC SNIPPETS │                   │
│  ├──────────────────┤    ├──────────────────┤                   │
│  │ • Pre-built HTML │    │ • Data-driven    │                   │
│  │ • 81+ templates  │    │ • ir.filters     │                   │
│  │ • Drag & drop    │    │ • Display temps  │                   │
│  └──────────────────┘    └──────────────────┘                   │
│                              │                                   │
│  ┌───────────────────────────┴────────────────────────────────┐ │
│  │                   SNIPPET OPTIONS SYSTEM                    │ │
│  ├────────────────────────────────────────────────────────────┤ │
│  │  we-select | we-button | we-colorpicker | we-input | etc.  │ │
│  │  data-selector | data-select-class | data-js | data-css    │ │
│  └────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

📋 静的スニペットの一覧(81 以上のテンプレート)

Odoo には、6 つの主なカテゴリーに分類された81 以上の静的スニペットテンプレートが用意されています。

カテゴリー数用途
構造14ページセクション、レイアウト
ギャラリー/メディア3画像ギャラリー、カルーセル
特徴11特徴の紹介
動的コンテンツ15地図、フォーム、埋め込み
内部コンテンツ16カード、引用、テキストブロック
メガメニュー9ナビゲーションメニュー
構造スニペット(14)
スニペット ID名前説明最適な用途
s_bannerバナー背景付きのヒーローセクションランディングページ
s_coverカバー全幅のカバー画像ヘッダー
s_text_imageテキスト - 画像2 列レイアウト:左にテキスト、右に画像特長
s_image_text画像 - テキスト2 列レイアウト:左に画像、右にテキスト特長
s_titleタイトルサブタイトル付きのセクションタイトルセクションヘッダー
s_text_blockテキストブロック複数列のテキストコンテンツコンテンツ
s_numbers数値統計・カウンターの表示実績
s_picture写真1 枚の大きな画像ビジュアル重視
s_three_columns列3 列レイアウトコンテンツグリッド
s_big_boxes大きなボックス大きな特長ボックスサービス
s_features特長アイコン付きの特長グリッドメリット
s_masonry_blockメイソンリーブロックメイソンリー形式のグリッドギャラリー
s_image_gallery画像ギャラリー複数画像のギャラリーポートフォリオ
s_images_wall画像ウォールウォール形式の表示メディア
特長スニペット(11)
スニペット ID名前説明最適な用途
s_comparisons比較並べて比較製品比較
s_company_team会社のチームチームメンバーのカード会社紹介ページ
s_call_to_action行動喚起ボタン付きの CTA セクションコンバージョン
s_references参照クライアントのロゴ・参照情報ソーシャルプルーフ
s_accordionアコーディオン折りたたみ可能なコンテンツよくある質問
s_features_grid特長グリッド特長アイコンのグリッドメリット
s_table_of_content目次スクロールスパイナビゲーション長いページ
s_pricelist価格表料金表製品
s_product_list製品一覧製品の紹介E コマース
s_faq_collapseFAQよくある質問サポート
s_tabsタブタブ形式のコンテンツ整理
動的コンテンツスニペット(15)
スニペット ID名前説明最適な用途
s_google_mapGoogle マップGoogle Maps の埋め込みお問い合わせページ
s_map地図Leaflet/OpenStreetMap場所
s_embed_code埋め込みコードカスタム HTML/埋め込みサードパーティ
s_website_formウェブサイトフォームお問い合わせフォーム/カスタムフォーム見込み客の獲得
s_searchbar検索バー検索機能ナビゲーション
s_social_mediaソーシャルメディアソーシャルリンクエンゲージメント
s_share共有共有ボタンコンテンツの共有
s_dynamic_snippet動的スニペット動的コンテンツの基本形データ表示
s_dynamic_snippet_carousel動的カルーセル動的カルーセル注目コンテンツ
s_chartグラフデータの可視化レポート
s_countdownカウントダウンタイマー・カウントダウンイベント
s_popupポップアップモーダルポップアッププロモーション
s_newsletter_blockニュースレターメール購読マーケティング
s_newsletter_popupニュースレターポップアップポップアップ形式の購読見込み客の獲得
s_newsletter_subscribe_formニュースレターフォームインライン形式の購読フッター
内部コンテンツスニペット(16)
スニペット ID名前説明最適な用途
s_hr水平線区切り線セクションの区切り
s_alertアラートアラート/通知ボックスお知らせ
s_cardカードコンテンツカード項目
s_three_cards3つのカード3カードレイアウトサービス
s_four_cards4つのカード4カードレイアウト機能
s_timelineタイムラインタイムライン表示沿革
s_process_stepsプロセスの手順手順ごとのガイドチュートリアル
s_quotes_carousel引用カルーセルお客様の声のスライダー社会的証明
s_quotes_grid引用グリッドお客様の声のグリッドレビュー
s_rating評価星評価の表示レビュー
s_progress_bar進捗バー進捗インジケーター統計
s_blockquote引用ブロック装飾付きの引用強調
s_badgeバッジバッジ/ラベルラベル
s_buttonボタン装飾付きのボタンCTA
s_separatorセパレーターセクションの区切り視覚的な区切り
s_text_highlightテキストの強調強調表示されたテキストボックス強調
メガメニュースニペット(9)
スニペット ID名前説明
s_mega_menu_multi_menus複数メニュー複数のメニュー列
s_mega_menu_menu_image_menuメニュー画像メニュー画像付きの3セクション
s_mega_menu_little_icons小さなアイコンアイコンベースのメニュー
s_mega_menu_images_subtitles画像とサブタイトルサブタイトル付きの画像
s_mega_menu_cardsカードカード形式のメニュー
s_mega_menu_big_icons大きなアイコン大きなアイコンのメニュー
s_mega_menu_thumbnailsサムネイルサムネイルギャラリーのメニュー
s_mega_menu_odoo_menuOdooメニューOdoo形式のメニュー
s_mega_menu_no_extra_info追加情報なしシンプルなメニュー

📋 動的スニペットシステム

動的スニペットはデータベースからデータを取得し、テンプレートを使って表示します。

アーキテクチャ
┌─────────────────────────────────────────────────────────────────┐
│                  DYNAMIC SNIPPET FLOW                            │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌──────────────────┐                                           │
│  │   DATA SOURCE    │ ← ir.filters / ir.actions.server          │
│  └────────┬─────────┘                                           │
│           │                                                      │
│           ▼                                                      │
│  ┌──────────────────┐                                           │
│  │ DYNAMIC FILTER   │ ← website.snippet.filter model            │
│  │ (Data Fetcher)   │                                           │
│  └────────┬─────────┘                                           │
│           │                                                      │
│           ▼                                                      │
│  ┌──────────────────┐                                           │
│  │ DISPLAY TEMPLATE │ ← dynamic_filter_template_*               │
│  │   (Renderer)     │                                           │
│  └──────────────────┘                                           │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘
商品の動的スニペット(website_sale)

メインスニペット: s_dynamic_snippet_products

表示テンプレート(15種類):

テンプレートキー名前スタイル
dynamic_filter_template_product_product_add_to_cartカートに追加購入ボタン付き
dynamic_filter_template_product_product_bannerバナー大きなバナー形式
dynamic_filter_template_product_product_borderless_1/2/3枠なしすっきりとした枠なしデザイン
dynamic_filter_template_product_product_card_styleカードスタイルカードベース
dynamic_filter_template_product_product_centered中央揃え中央揃えのレイアウト
dynamic_filter_template_product_product_horizontal_card横型カード水平レイアウト
dynamic_filter_template_product_product_miniミニコンパクトな表示
dynamic_filter_template_product_product_minimalist_1/2/3/4ミニマリストミニマルなデザイン
dynamic_filter_template_product_product_picture画像画像を重視

動的フィルター(6つのデータソース):

フィルター ID名前ロジック
dynamic_filter_newest_products最新の商品create_dateの降順で並べ替え
dynamic_filter_recently_sold_products最近販売された商品最近売れた商品
dynamic_filter_recently_viewed_products最近閲覧した商品ユーザーの閲覧履歴
dynamic_filter_accessoriesアクセサリー関連アクセサリー
dynamic_filter_products_sold_with一緒に購入された商品よく一緒に購入される商品
dynamic_filter_alternative_products代替商品類似商品
ブログの動的スニペット(website_blog)

メインスニペット: s_blog_posts

表示テンプレート(4つのレイアウト):

テンプレートキー名前
dynamic_filter_template_blog_post_big_picture大きな画像
dynamic_filter_template_blog_post_cardカード
dynamic_filter_template_blog_post_horizontal横型
dynamic_filter_template_blog_post_vertical縦型

フィルター: dynamic_filter_latest_posts、dynamic_filter_most_read_posts

イベントの動的スニペット(website_event)

メインスニペット:s_events

表示テンプレート:dynamic_filter_template_event_event_card、dynamic_filter_template_event_event_list

フィルター:dynamic_filter_upcoming_events


📋 スニペットオプションシステム

オプションシステムを使用すると、ユーザーはウェブサイトエディターでスニペットをカスタマイズできます。

We-* 要素リファレンス
要素目的主な属性
we-selectドロップダウン選択data-name、data-dependencies
we-button切り替えボタンdata-select-class、data-toggle-class
we-colorpickerカラーピッカーdata-css-property、data-color-prefix
we-inputテキスト/数値入力data-attribute-name、data-unit
we-rangeスライダーdata-min、data-max、data-step
we-checkboxチェックボックスの切り替えdata-select-class
we-button-groupボタンのグループ配置などに使用
we-rowオプションの行関連する入力をまとめます
we-select-pagerページ分けされた選択多数のオプションがある場合に使用
データ属性リファレンス
属性目的例
data-selector対象の CSS セレクターdata-selector=".s_banner"
data-select-classCSS クラスの切り替えdata-select-class="shadow-lg"
data-select-styleスタイル編集を有効化data-select-style="true"
data-css-property変更する CSS プロパティdata-css-property="padding"
data-jsJavaScript ハンドラークラスdata-js="BackgroundImage"
data-dependencies他のオプションに応じて表示data-dependencies="image_opt"
data-exclude特定の要素では非表示data-exclude=".no-shadow"
data-no-previewライブプレビューを無効化data-no-preview="true"
data-attribute-name設定する HTML 属性data-attribute-name="data-speed"
組み込み JavaScript ハンドラー
ハンドラー目的
BackgroundImage背景画像の管理
BackgroundPosition画像の配置
BackgroundToggler背景タイプの切り替え
ColoredLevelBackgroundカラーパレットの背景
BackgroundShape装飾的なシェイプ
ImageTools画像編集
ReplaceMediaメディアの置き換え
FontawesomeToolsアイコンの管理
Carouselカルーセルのコントロール
DynamicSnippet動的コンテンツのオプション
オプションテンプレートの完全な例
<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"
             data-drop-near="section">

            
            <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-name="bg_color_opt"
                            data-select-style="true"
                            data-css-property="background-color"/>

            
            <we-range string="Spacing"
                      data-name="spacing_opt"
                      data-select-style="true"
                      data-css-property="gap"
                      data-min="0"
                      data-max="100"
                      data-step="5"
                      data-unit="px"/>

            
            <we-select string="Shadow Size"
                       data-name="shadow_size_opt"
                       data-dependencies="shadow_opt">
                <we-button data-select-class="shadow-sm">Small</we-button>
                <we-button data-select-class="shadow-lg">Large</we-button>
            </we-select>

        </div>
    </xpath>
</template>

📋 カスタムスニペットの作成(バージョン対応)
Odoo 14-17:シンプルな登録

<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>


<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:スニペットグループを使用



<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>


<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>
スニペットパネルのカテゴリー
パネル IDカテゴリーXPath ターゲット
snippet_mega_menuメガメニュー//div[@id='snippet_mega_menu']
snippet_structure構造//div[@id='snippet_structure']
snippet_mediaギャラリー//div[@id='snippet_media']
snippet_feature機能//div[@id='snippet_feature']
snippet_dynamic動的コンテンツ//div[@id='snippet_dynamic']
snippet_inner_content内部コンテンツ//div[@id='snippet_inner_content']

📋 スニペットの JavaScript 統合
スニペット向け publicWidget パターン
/** @odoo-module **/

import publicWidget from "@web/legacy/js/public/public_widget";

publicWidget.registry.MySnippet = publicWidget.Widget.extend({
    selector: '.s_my_snippet',
    disabledInEditableMode: false,  // Allow in website builder

    events: {
        'click .load-more': '_onLoadMore',
    },

    start: function () {
        // CRITICAL: Check editableMode for builder compatibility
        if (!this.editableMode) {
            this._initializeAnimations();
        }
        return this._super(...arguments);
    },

    _initializeAnimations: function () {
        // Animation code that should NOT run in edit mode
        this.$el.addClass('animated');
    },

    _onLoadMore: function (ev) {
        ev.preventDefault();
        if (this.editableMode) return;  // Don't run in edit mode
        // Handler logic
    },

    destroy: function () {
        // CRITICAL: Clean up event listeners
        $(window).off('.mySnippet');
        this._super(...arguments);
    },
});

export default publicWidget.registry.MySnippet;
スニペットオプションの JavaScript ハンドラー
/** @odoo-module **/

import options from "@web_editor/js/editor/snippets.options";

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

    _applyLayout: function(layout) {
        this.$target.removeClass('layout-grid layout-list');
        this.$target.addClass(`layout-${layout}`);
    },

    /**
     * Compute option visibility
     */
    _computeWidgetVisibility: function(methodName, params) {
        if (methodName === 'showAdvanced') {
            return this.$target.hasClass('advanced-mode');
        }
        return this._super(...arguments);
    },
});

📋 スニペットのバージョンごとの差異
機能Odoo 14-15Odoo 16-17Odoo 18-19
スニペット数約4081以上155以上
登録方法シンプルな XPathシンプルな XPathグループが必須
アセットシステムテンプレートの継承ir.asset モデルir.asset モデル
JavaScriptodoo.define()ES6 モジュールプラグインベース
Bootstrap4.x5.1.35.1.3
動的スニペット基本完全対応完全対応+カテゴリー
エディターレガシーレガシープラグインアーキテクチャ
Odoo 19 のプラグインパターン(新規)
import { Plugin } from "@html_editor/plugin";
import { registry } from "@web/core/registry";

export class MySnippetPlugin extends Plugin {
    static id = "mySnippet";
    static dependencies = ["builderOptions", "builderActions"];

    resources = {
        builder_options: [{
            template: "my_module.MySnippetOption",
            selector: "section",
            applyTo: ".s_my_snippet",
        }],
        builder_actions: {
            MyAction: { /* action definition */ },
        },
    };
}

registry.category("website-plugins").add("mySnippet", MySnippetPlugin);

📋 動的スニペットの作成
ステップ 1:フィルターを定義
<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>

<record id="ir_filter_my_items" model="ir.filters">
    <field name="name">Latest Items</field>
    <field name="model_id">my.model</field>
    <field name="domain">[('is_published', '=', True)]</field>
    <field name="sort">create_date desc</field>
</record>
ステップ 2:表示テンプレートを作成
<template id="dynamic_filter_template_my_model_card" name="My Model 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"/>
                    <p class="card-text" t-esc="record.description"/>
                </div>
            </div>
        </div>
    </t>
</template>
ステップ 3:動的スニペットを作成する
<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-name="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"
             data-number-of-elements-small-devices="2">
        <div class="container">
            <div class="row">
                <h2 class="col-12 text-center mb-4">Featured Items</h2>
            </div>
            <div class="dynamic_snippet_template row"/>
        </div>
    </section>
</template>

更新された /create-theme コマンド(v6.0)

/create-theme コマンドは、テンプレートの有効化機能を備えた models/theme_xxx.py ファイルを自動生成するようになりました。

作成されるもの(v6.0)
theme_<name>/
├── __init__.py                        # Imports models
├── __manifest__.py                    # Updated with models/ import
├── models/
│   ├── __init__.py                   # Imports theme_<name>
│   └── theme_<name>.py               # theme.utils implementation (NEW!)
├── security/
│   └── ir.model.access.csv
├── data/
│   ├── assets.xml
│   ├── menu.xml
│   └── pages/
│       ├── home_page.xml
│       ├── aboutus_page.xml
│       └── contactus_page.xml
├── views/
│   ├── layout/
│   │   └── templates.xml
│   └── snippets/
│       └── custom_snippets.xml
└── static/src/
    ├── scss/
    │   ├── primary_variables.scss
    │   └── theme.scss
    ├── js/
    │   └── theme.js
    └── img/
生成される theme_.py
from odoo import models


class Theme<Name>(models.AbstractModel):
    _inherit = 'theme.utils'

    def _theme_<name>_post_copy(self, mod):
        """
        Configure theme defaults when installed on a website.

        This method is called automatically when the theme is applied.
        Use enable_view() and disable_view() to configure templates.
        """
        # === HEADER CONFIGURATION ===
        # Enable desired header template (only one can be active)
        self.enable_view('website.template_header_<detected>')

        # Configure header visibility
        self.disable_view('website.header_visibility_standard')
        self.enable_view('website.header_visibility_fixed')

        # Enable header components
        self.enable_view('website.option_header_brand_logo')

        # === FOOTER CONFIGURATION ===
        # Enable desired footer template (only one can be active)
        self.enable_view('website.template_footer_<detected>')

変更履歴

  • v7.0.0:Webサイトスニペット完全リファレンス(メジャー)

    • 新機能:静的スニペットの完全な一覧(81 個以上のテンプレート)
      • 構造スニペット(s_banner、s_cover、s_parallax など)
      • 機能スニペット(s_features、s_comparisons、s_image_text など)
      • 動的コンテンツスニペット(s_dynamic_snippet_*、products、blog、events)
      • 内部コンテンツスニペット(s_hr、s_badge、s_card、s_blockquote など)
      • メガメニュースニペット(s_mega_menu_*、multi_menus、odoo_menu)
    • 新機能:動的スニペットシステムのドキュメント
      • website.snippet.filter モデルのアーキテクチャ
      • 15 個の商品表示テンプレート(dynamic_filter_template_*)
      • 6 個の組み込み商品スニペットフィルター
      • ブログとイベントの動的スニペットパターン
    • 新機能:スニペットオプションシステムのリファレンス
      • we-* 要素の完全な一覧(we-select、we-button、we-colorpicker、we-input、we-range、we-checkbox)
      • データ属性のリファレンス(data-selector、data-select-class、data-js、data-css-property、data-dependencies)
      • 組み込み JavaScript ハンドラー(BackgroundImage、Carousel、ImageTools など)
      • スニペットオプションの完全な XML 例
    • 新機能:バージョンに応じたカスタムスニペットの作成
      • Odoo 14~17 のシンプルな t-snippet 登録
      • Odoo 18~19 のスニペットグループパターン
      • XPath 挿入テクニック
    • 新機能:Odoo 19 プラグインパターンのドキュメント
      • @html_editor/plugin ベースのアーキテクチャ
      • 130 個以上のプラグインファイルのリファレンス
      • SnippetOption クラスパターン
    • 新機能:動的スニペット作成ガイド
      • フィルターを定義する(website.snippet.filter)
      • 表示テンプレートを作成する(dynamic_filter_template_*)
      • data-filter-id を指定して動的スニペットを作成する
  • v6.0.0:テーマ機能の有効化システムと動的ページのリファレンス(メジャー)

    • 新機能:テーマ機能の有効化システム
      • theme.utils モデルの完全なドキュメント
      • _theme_{module}_post_copy() パターンの要件
      • enable_view() および disable_view() メソッドのリファレンス
      • ヘッダー/フッターの相互排他ルール
      • 動作する完全な例
    • 新機能:動的ページの完全なリファレンス
      • XML ID と視覚的な図を含む 11 個のヘッダーテンプレート
      • XML ID と視覚的な図を含む 9 個のフッターテンプレート
      • すべてのショップページテンプレート(カテゴリー、フィルター、レイアウト)
      • すべての商品詳細ページテンプレート
      • すべてのブログテンプレート(一覧、詳細、サイドバー)
      • カートおよびチェックアウトのテンプレート
    • 新機能:デザインワークフローの方法論
      • Figma 分析情報の抽出ガイド
      • テンプレート選択の判断フローチャート
      • デザインに基づく設定の生成
      • 手順ごとのワークフロードキュメント
    • 更新:/create-theme コマンド v6.0
      • models/theme_xxx.py を自動生成するようになりました
      • 設定に基づいてテンプレートを有効化
      • 必要なすべてのファイルを含む完全なモジュール構成
  • v5.1.0:変数リファレンスを包括的に拡充(大幅な変更)

    • $o-theme-font-configs の完全なリファレンス:
      • 'family':CSS の font-family リスト(タプル形式)
      • 'url':Google Fonts のパラメーターのみ(完全な URL ではありません)
      • 'properties':コンテキストごとの CSS オーバーライド(base、headings、navbar、buttons)
      • フォントエイリアスの説明:'base'、'headings'、'h2'-'h6'、'navbar'、'buttons'
      • アラビア語/RTL フォントのサポート例
    • $o-color-palettes の完全なリファレンス:
      • 5 つの基本色の意味(o-color-1 から o-color-5)
      • 色の組み合わせ(o_cc1 - o_cc5)のプリセットと使用方法
      • 色の組み合わせのオーバーライド構文('o-cc{n}-{property}')
      • コンポーネントへの割り当て(メニュー、フッター、著作権表示)
      • 色クラスを使った HTML の使用例
    • $o-website-values-palettes の完全なリファレンス(115 以上のキー):
      • タイポグラフィとフォント(13 キー):フォントファミリーの設定
      • フォントサイズ(13 キー):基本サイズと見出しのサイズ
      • 行の高さとマージン(33 キー):間隔の設定
      • ボタン(17 キー):パディング、角丸、スタイルのオプション
      • 入力欄(12 キー):フォームフィールドのスタイル設定
      • ヘッダー(13 キー):テンプレート、リンクのスタイル、ロゴ、ハンバーガーメニュー
      • フッター(3 キー):テンプレート、エフェクト、スクロールトップ
      • リンク(1 キー):下線の動作
      • レイアウト(3 キー):全幅/ボックス型、背景
      • 色とグラデーション(5 キー):パレットの選択、グラデーション
      • Google Fonts(2 キー):追加フォント
    • テーマの完全な構造を復元:header.xml、footer.xml、スニペット(任意ですが、階層内に存在します)
    • 重複するドキュメントのセクションを削除:重複した内容を整理
  • v5.0.0:包括的な変数システムの分析に基づく大幅な機能拡張

    • カスタム XML を使わずに、変数でヘッダー/フッターのテンプレートを制御できます
    • 'header-template' および 'footer-template' 変数で設定できます
    • $o-website-values-palettes で Bootstrap を制御できます
  • v4.0.0:実際のテーマ開発で発生した問題に基づく重大な修正

    • ⚠️ SCSS の読み込み順に関する説明:テーマの SCSS がコア変数より前に読み込まれることを説明
    • map-merge() パターンを削除:コア変数と map-merge() を使用していた例を修正(「Undefined variable」エラーの原因となります)
    • フォント読み込みの修正:ir.asset レコード経由ではなく、単独で $o-theme-font-configs を使用
    • XPath を簡素化:複雑な式(//header//nav)ではなく、単純な式(//header)を使用
    • トラブルシューティングを強化:SCSS のデバッグ、アセットキャッシュのクリア、コンパイルが通知なしに失敗するケースを追加
    • すべてのテンプレートを修正:/create-theme コマンドが map-merge に関する問題のない、動作するコードを生成
  • v3.1.0:40 件以上の実装例に基づき、テーマを完全に生成する /create-theme コマンドを追加

    • $o-website-values-palettes の完全な設定
    • セマンティックカラーシステム(o-color-1 から o-color-5)
    • 個別のページファイルパターン(ベストプラクティス)
    • editableMode に対応した publicWidget パターン
    • バージョン別のスニペット登録(14-19)
    • スクリプト出力時の Windows コンソール文字エンコーディングを修正
  • v3.0.0:$o-website-values-palettes のリファレンスとミラーモデルアーキテクチャでテーマシステムを強化

  • v2.0.0:PWA サポート、TypeScript、テストフレームワーク、パフォーマンスの最適化、アクセシビリティ、リアルタイム機能、最新のビルドツールを追加

  • v1.0.0:Odoo 14-19 の完全なサポートと Figma/DevTools MCP 統合を備えた初回リリース

コメント