odoo-frontend について
テーマ生成、バージョン検出、テーマ設定、フロントエンド開発手法を扱う Odoo フロントエンド開発スキルです。Odoo 14~19 の違いにも対応し、Odoo のウェブサイトやテーマを開発・保守する開発者向けです。
主な機能
- 01/create-theme でテーマモジュールを生成
- 02Odoo バージョンを検出して Bootstrap を対応付け
- 03テーマ変数と動的ページテンプレートを参照
- 04JavaScript、PWA、テスト、パフォーマンスを網羅
- 05Figma デザイン変換と Chrome DevTools のスタイル抽出
活用シーン
- 01Odoo ウェブサイトのテーマモジュールを作成
- 02Odoo のバージョンに応じた実装方法を選択
- 03Figma のデザインを Odoo テンプレートに変換
- 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.pycreate_theme.pydevtools_extractor.pyfigma_converter.pypwa_generator.pytheme_mirror_generator.pyversion_detector.pytemplates/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"
重要なルール
odoo/またはodoo/addons/ディレクトリ内の Odoo コアは絶対に編集しないでください- インライン JS/CSS は使用せず、個別の
.jsファイルと.scssファイルを作成してください - JS モジュール:
/** @odoo-module **/アノテーションを使用してください - ウェブサイトテーマ:Owl やバニラ JS ではなく、
publicWidgetフレームワークのみを使用してください - Bootstrap:Odoo 16 以降では v5.1.3 のクラスを使用してください(Tailwind は使用しないでください)
- モジュール名:
snake_case規則を使用してください - 翻訳:静的なラベルは、実行時のラッパーではなく、定義時に
_t()を使って JS の配列/定数で囲んでください。静的な XML 文字列は自動的に翻訳されます。
自動検出ワークフロー
ステップ 1:Odoo のバージョンを検出する
フロントエンドのタスクを開始するときは、まず Odoo のバージョンを検出してください。
# Use the version detector script
python scripts/version_detector.py <module_path>
バージョン検出機能は、次の処理を行います。
__manifest__.pyまたは__openerp__.pyを探します- バージョンフィールドを解析します
- 見つからない場合は、親ディレクトリ(例:
odoo17/)から検出します - 次の情報を返します。
- 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_cc1 | o-color-4(白) | メインコンテンツ領域 |
o_cc2 | o-color-3(明るい色) | 交互に配置するセクション |
o_cc3 | o-color-2(セカンダリ) | アクセントセクション |
o_cc4 | o-color-1(プライマリ) | 行動喚起セクション |
o_cc5 | o-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 Fonts | 2 | 追加フォントの読み込み |
| 合計 | 115以上 |
3.1 タイポグラフィとフォント
| Key | Type | Default | Description |
|---|---|---|---|
'font' | string | First in config | サイト全体の基本フォント |
'headings-font' | string | Inherits font | H1~H6 見出しのフォント |
'navbar-font' | string | Inherits font | ナビゲーションメニューのフォント |
'buttons-font' | string | Inherits font | ボタンテキストのフォント |
'h2-font' - 'h6-font' | string | Inherits headings | 個別の見出しフォント |
'display-1-font' - 'display-4-font' | string | Inherits headings | ディスプレイテキストのフォント |
3.2 フォントサイズ
| Key | Type | Default | Description |
|---|---|---|---|
'font-size-base' | size | 1rem | 基本フォントサイズ(16px) |
'small-font-size' | size | 0.875rem | 小さなテキスト(14px) |
'h1-font-size' - 'h6-font-size' | size | Calculated | 見出しのサイズ |
'display-1-font-size' - 'display-6-font-size' | size | 5rem-2.5rem | ディスプレイサイズ |
3.3 行の高さとマージン
| Key | Type | Default | Description |
|---|---|---|---|
'body-line-height' | number | 1.5 | 本文の行の高さ |
'headings-line-height' | number | 1.2 | すべての見出しの行の高さ |
'h2-line-height' - 'h6-line-height' | number | Inherits | 個別の見出しの行の高さ |
'paragraph-margin-top' | size | 0 | 段落の上マージン |
'paragraph-margin-bottom' | size | 16px | 段落の下マージン |
'headings-margin-top' | size | 0 | 見出しの上マージン |
'headings-margin-bottom' | size | 0.5rem | 見出しの下マージン |
'h2-margin-top' - 'h6-margin-top' | size | Inherits | 個別のマージン |
'h2-margin-bottom' - 'h6-margin-bottom' | size | Inherits | 個別のマージン |
3.4 ボタン(17 個のキー)
| Key | Type | Default | Description |
|---|---|---|---|
'btn-padding-y' | size | Bootstrap | 垂直方向のパディング |
'btn-padding-x' | size | Bootstrap | 水平方向のパディング |
'btn-font-size' | size | Bootstrap | フォントサイズ |
'btn-padding-y-sm' / 'btn-padding-x-sm' | size | Bootstrap | 小さいボタンのパディング |
'btn-padding-y-lg' / 'btn-padding-x-lg' | size | Bootstrap | 大きいボタンのパディング |
'btn-font-size-sm' / 'btn-font-size-lg' | size | Bootstrap | サイズバリエーション |
'btn-border-width' | size | Bootstrap | 枠線の太さ |
'btn-border-radius' | size | Bootstrap | 角の丸み |
'btn-border-radius-sm' / 'btn-border-radius-lg' | size | Bootstrap | サイズ別の角の丸み |
'btn-primary-outline' | boolean | false | プライマリをアウトライン表示 |
'btn-secondary-outline' | boolean | false | セカンダリをアウトライン表示 |
'btn-primary-flat' | boolean | false | フラットなプライマリスタイル |
'btn-secondary-flat' | boolean | false | フラットなセカンダリスタイル |
'btn-ripple' | boolean | false | Material Design のリップル効果 |
3.5 入力欄とフォーム(12 個のキー)
| Key | Type | Default | Description |
|---|---|---|---|
'input-padding-y' / 'input-padding-x' | size | Bootstrap | 入力欄のパディング |
'input-font-size' | size | Bootstrap | 入力欄のフォントサイズ |
'input-padding-y-sm' / 'input-padding-x-sm' | size | Bootstrap | 小さい入力欄のパディング |
'input-padding-y-lg' / 'input-padding-x-lg' | size | Bootstrap | 大きい入力欄のパディング |
'input-border-width' | size | Bootstrap | 枠線の太さ |
'input-border-radius' | size | Bootstrap | 角の丸み |
'input-border-radius-sm' / 'input-border-radius-lg' | size | Bootstrap | サイズ別の角の丸み |
3.6 ヘッダーとナビゲーション(13 個のキー)
| キー | 型 | 値 | デフォルト | 説明 |
|---|---|---|---|---|
'header-template' | string | 以下を参照 | 'default' | ヘッダーのレイアウト |
'header-links-style' | string | 以下を参照 | 'default' | ナビゲーションリンクのスタイル |
'header-font-size' | size | CSS length | Bootstrap | ヘッダーテキストのサイズ |
'logo-height' | size | CSS length | Navbar height | ロゴの高さ |
'fixed-logo-height' | size | CSS length | Smaller | 固定表示時のロゴの高さ |
'hamburger-position' | string | 'left'/'center'/'right' | 'left' | デスクトップでの位置 |
'hamburger-position-mobile' | string | 'left'/'center'/'right' | 'left' | モバイルでの位置 |
'menu-border-width' | size | CSS length | null | メニューの枠線 |
'menu-border-radius' | size | CSS length | null | メニューの角 |
'menu-box-shadow' | CSS | shadow/none | null | メニューの影 |
'sidebar-width' | size | CSS length | 18.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' | boolean | true/false | false | ページトップへ戻るボタン |
フッターテンプレートのオプション:
'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' | string | null | 使用中のカラーパレット名 |
'menu-gradient' | CSS gradient | null | メニューの背景グラデーション |
'menu-secondary-gradient' | CSS gradient | null | セカンダリメニューのグラデーション |
'footer-gradient' | CSS gradient | null | フッターの背景グラデーション |
'copyright-gradient' | CSS gradient | null | 著作権表示のグラデーション |
3.11 Google Fonts(2 個のキー)
| キー | 型 | デフォルト | 説明 |
|---|---|---|---|
'google-fonts' | list | null | 読み込む追加の Google Fonts |
'google-local-fonts' | map | null | ローカルでホストされるフォント |
完全な例 - モダンな企業テーマ
$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の割り当てはビューの作成時に行われます
テーマのスキャフォールディングコマンド
コマンド:完全なテーマモジュールをスキャフォールディングする
トリガー:ユーザーが「テーマを作成」「テーマをスキャフォールディング」「テーマモジュールを生成」と依頼した場合
ワークフロー:
-
コンテキストを検出
# 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('.'))" -
モジュール構造を作成
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 -
__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, } -
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', ) ); -
初期テンプレートを生成
<?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>
コマンド:スニペットを作成(バージョン対応)
トリガー:ユーザーが「スニペットを作成」「カスタムスニペットを追加」と依頼した場合
ワークフロー:
-
Odoo のバージョンを検出(登録方法を決定します)
-
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> -
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> -
スニペットのオプションを生成(必要な場合)
<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> -
オプション用 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 が利用可能であること
ワークフロー:
-
バージョン固有のプロンプトで 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) -
カラーパレットを抽出
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. -
HTML を QWeb テンプレートに変換
<template>タグで囲みますt-name属性を追加します- Odoo エディター用のデータ属性を追加します
- セクションベースの構造に変換します
-
Figma の色から SCSS を生成
python scripts/figma_converter.py <figma_url> <output_dir> <odoo_version> -
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 が利用可能であること
ワークフロー:
-
DevTools MCP に接続する
- Chrome DevTools MCP で URL を開く
- 抽出する要素を選択する
-
計算済みスタイルを抽出する
python scripts/devtools_extractor.py <url> <output_dir> -
Odoo SCSS に変換する
- CSS プロパティを SCSS 変数に対応付ける
- 色を Odoo のテーマ変数(o-color-*)に変換する
- 適用できる場合は Bootstrap 変数を使用する
-
Bootstrap ユーティリティに対応付ける
- 同等の Bootstrap クラスを特定する
- 可能な場合はカスタム CSS をユーティリティに置き換える
- クラスベースのアプローチに関する提案を生成する
-
DOM を QWeb に変換する
- HTML 構造を抽出する
- Odoo 固有の属性を追加する
- 適切なテンプレート構造で囲む
- Bootstrap グリッド構造を追加する
-
テーマコード全体を生成する
- 抽出したスタイルを含む 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 の重要なポイント:
- アニメーションやインタラクションを実行する前に、必ず
this.editableModeを確認する - Web サイトビルダーでウィジェットを動作させるには、
disabledInEditableMode: falseを使用する destroy()メソッドでイベントリスナーを必ずクリーンアップする- Web サイトのテーマでは、Owl やバニラ JavaScript は絶対に使用せず、publicWidget のみを使用する
- 簡単にクリーンアップできるよう、名前空間付きイベント(
.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() を定義時に使用する場合:
- ✅ JavaScript 配列内の静的ラベル(月名、曜日名、ステータスラベル)
- ✅ JavaScript オブジェクトや定数内の静的ラベル
- ✅ 定数として定義されたエラーメッセージ
- ✅ JavaScript からユーザーに表示される文字列リテラル
_t() を使用しない場合:
- ❌ XML テンプレート内に直接記述された静的テキスト(自動翻訳される)
- ❌
translateLabel(key)のような実行時のラッパーメソッド(機能しません!) - ❌ 実行時に
_t()に渡される動的変数 - ❌
.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 固有の事項
-
Owl v1
@odoo/owlからuseStateを使用します- レガシーなインポートパス
- コンポーネントのテンプレートを個別のプロパティとして指定します
-
スニペットの構造
- グループを使わずにシンプルに挿入します
//div[@id='snippet_effect']をターゲットとする XPath を使用します
-
パブリックウィジェット
@web/legacy/js/public/public_widgetからインポートします- jQuery を全面的に利用できます
Odoo 18/19 固有の事項
-
Owl v2
- 静的なテンプレートプロパティ
- Props の検証が必須です
- 新しいリアクティブフック
- v1 からの破壊的変更があります
-
スニペットグループ
snippet-group属性が必須です- カテゴリーごとに整理します
//div[@id='snippet_structure']をターゲットとする XPath を使用します
-
Bootstrap 5.1.3
- すべて同じ Bootstrap バージョンを使用します
- ユーティリティクラスが統一されています
-
ウェブサイトビルダーの機能強化
- エディターでテーマを閲覧できます
- UI からカスタムフォントをアップロードできます
- スニペットのオプションが強化されています
Bootstrap 4 から 5 への移行(Odoo 14/15 → 16+)
テーマを移行する場合:
-
クラスの置き換え:
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-starttext-right→text-endfloat-left→float-startfloat-right→float-endform-group→mb-3custom-select→form-selectclose→btn-closebadge-*→bg-*(例:badge-primary→bg-primary)font-weight-bold→fw-boldsr-only→visually-hiddenno-gutters→g-0
-
削除されたクラス(代替手段を探してください):
form-inline- グリッドまたはフレックスのユーティリティを使用しますjumbotron- ユーティリティを使って再現しますmedia- フレックスのユーティリティとともにd-flexを使用します
-
jQuery の削除:
- Bootstrap 5 ではバニラ JavaScript を使用します
- ウィジェットの初期化を調整する必要がある場合があります
フロントエンドのパフォーマンスに関するベストプラクティス
-
アセットのバンドル
- 常に Odoo のアセットバンドルを使用します
- 外部ファイルを個別にリンクしないでください
- 本番環境で有効になる圧縮を活用します
-
遅延読み込み
- 画像に
loading="lazy"を使用します - 自動リサイズには Odoo の
/website/imageルートを活用します - 動画を遅延読み込みします(Odoo 18 以降では組み込みでサポートされています)
- 画像に
-
スニペットの最適化
- サーバー呼び出しをまとめます(すべてのデータに対して RPC を 1 回実行します)
- 効率的なレンダリングには QWeb を使用します
- DOM 操作を最小限に抑えます
-
CSS の最適化
- カスタム CSS の代わりに Bootstrap のユーティリティを使用します
- HTML 構造を簡潔に保ちます
- バンドルから未使用の CSS/JS を削除します
-
キャッシュ
store=Trueを指定した計算フィールドを活用します- コストの高い計算結果をキャッシュします
- 負荷の高い処理はサーバー側で行います
-
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 から完全なテーマを作成する
- ユーザーから Figma の URL を受け取ります
- 対象となる Odoo のバージョンを検出します
- Figma MCP を使用してデザインを抽出します
- テーマモジュールの構造を生成します
- 色を SCSS 変数に変換します
- コンポーネントをスニペットに変換します
- スニペットのオプションを作成します
- Odoo ウェブサイトビルダーでテストします
既存のテーマを拡張する
- 現在のテーマとバージョンを検出します
- 既存の構造を分析します
- 継承するモジュールを作成します
- 特定のテンプレートをオーバーライドします
- カスタムスニペットを追加します
- SCSS 変数を拡張します
バージョン間でのテーマの移行
- 移行元と移行先のバージョンを検出します
- Bootstrap クラスを変換します(4→5 の場合)
- スニペットの構造を更新します(17→18/19 の場合)
- JavaScript を更新します(Owl v1→v2 の場合)
- すべての機能をテストします
- 移行レポートを生成します
テストと検証
テーマのインストールテスト
# 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
スニペット登録テスト
- テーマをインストールします
- Website → Edit に移動します
- スニペットパネルを確認します
- スニペットが正しいカテゴリに表示されることを確認します
- ドラッグ&ドロップをテストします
- スニペットのオプションをテストします
ブラウザテスト
- 複数のブラウザ(Chrome、Firefox、Safari、Edge)でテストします
- レスポンシブブレークポイント(sm、md、lg、xl、xxl)をテストします
- アクセシビリティ(ARIA ラベル、キーボードナビゲーション)を検証します
- パフォーマンスを確認します(Lighthouse、Page Speed)
トラブルシューティング
⚠️ SCSS のコンパイルエラー(重大)
問題:「Undefined variable: $o-color-palettes」または「$o-theme-font-configs」
原因: テーマの SCSS で、コアの Odoo 変数とともに map-merge() を使用しています。テーマファイルはコア変数が定義される前に読み込まれます。
解決策:
- コア変数を使った
map-merge()の呼び出しをすべて削除します $o-theme-font-configsを単独で定義します(マージしません)- 既存のパレットを参照するには
'color-palettes-name': 'default-1'を使用します - 上記の「重大:SCSS の読み込み順」セクションを参照してください
問題:CSS のルール数が 0 と表示される/スタイルが読み込まれない
原因: 未定義の変数による SCSS コンパイルのサイレントな失敗です。
診断:
// In browser console
document.styleSheets[0].cssRules.length // Should be > 0
// If 0, SCSS compilation failed silently
解決策:
- ブラウザーコンソールで「Style compilation failed」エラーを確認します
- 未定義の変数エラーを修正します(上記を参照)
- アセットキャッシュをクリアします:
# 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 の構文エラー
解決策:
- Odoo のバージョンを確認し、適切な挿入方法を使用します
- テンプレート ID が存在することを確認します
- XML の構文を確認します
- ログでエラーを確認します
スタイルが適用されない
原因:
- SCSS が正しいバンドルに含まれていない
- 変数のオーバーライドに
!defaultが使われていない - CSS の詳細度の問題
- アセットがコンパイルされていない
- SCSS のコンパイルがサイレントに失敗している(コンソールを確認してください!)
解決策:
- マニフェストのアセットバンドルを確認します
- ブラウザーキャッシュをクリアします
- アセットの再生成を確認します:
odoo-bin --update theme_<name> - ブラウザーの DevTools で計算済みスタイルを調べます
- まずコンソールで SCSS エラーを確認してください!
JavaScript が動作しない
原因:
- Odoo のバージョンに対してインポートパスが正しくない
- ウィジェットが登録されていない
- jQuery の競合(Bootstrap 5)
- Owl のバージョンが一致していない
解決策:
- Odoo のバージョンに対応したインポートパスを確認します
- ウィジェット/コンポーネントの登録を確認します
- コンソールでエラーを確認します
- 正しい Owl のバージョンを使用していることを確認します
ヘッダー/フッターのテンプレートで XPath エラーが発生する
原因: //header//nav のような複雑な XPath 式が、Odoo の実際の HTML 構造に一致しない場合があります。
解決策: シンプルな XPath 式を使用します:
<xpath expr="//header" position="attributes">
<xpath expr="//header//nav" position="attributes">
エキスパート向けのヒント
-
必ず最初にバージョンを検出する
- Odoo のバージョンを決めつけないでください
- コードを生成する前に確認してください
-
Bootstrap ユーティリティを優先する
- カスタム CSS を減らすと、メンテナンスが容易になります
- パフォーマンスが向上します
- エディターとの互換性が高まります
-
SEO にはサーバーサイドレンダリングを使用する
- コンテンツをクライアント側の JS に依存させないでください
- Owl は機能強化のみに使用します
-
編集モードでテストする
- Website Builder がコードを処理できることを確認します
- スニペットのオプションを十分にテストします
-
Odoo のパターンに従う
- コアテーマ(
odoo/addons/theme_*)を調べます - 既存の規約に合わせます
- 同じクラス接頭辞(
s_、o_)を使用します
- コアテーマ(
-
ドキュメント
- 複雑な SCSS にコメントを付けます
- スニペットのオプションを文書化します
- テーマに README を用意します
プログレッシブウェブアプリ(PWA)の実装
コマンド:Odoo Website 用 PWA のセットアップ
トリガー:ユーザーが「PWA を作成」「オフラインサポートを追加」「プッシュ通知を有効化」と依頼した場合
ワークフロー:
-
サービスワーカーを生成
// 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)) ); }); -
Web アプリマニフェストを作成
{ "name": "Odoo PWA", "short_name": "OdooPWA", "start_url": "/", "display": "standalone", "background_color": "#875A7B", "theme_color": "#875A7B", "icons": [...] } -
サービスワーカーを登録
/** @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 を設定」と依頼した場合
ワークフロー:
-
tsconfig.json を生成
{ "compilerOptions": { "target": "ES2020", "module": "ES2020", "lib": ["ES2020", "DOM"], "strict": true, "paths": { "@web/*": ["./addons/web/static/src/*"], "@owl/*": ["./addons/web/static/lib/owl/*"] } } } -
型付き 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 を設定」と依頼した場合
ワークフロー:
-
Jest の設定
// jest.config.js module.exports = { testEnvironment: 'jsdom', moduleNameMapper: { '^@web/(.*)$': '<rootDir>/addons/web/static/src/$1', }, collectCoverageFrom: ['static/src/**/*.{js,jsx}'] }; -
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(); }); }); -
ビジュアルリグレッションテスト
// backstop.config.js module.exports = { scenarios: [{ label: 'Homepage', url: 'http://localhost:8069', misMatchThreshold: 0.1 }] };
パフォーマンスの最適化
コマンド:Core Web Vitals の最適化
トリガー:ユーザーが「パフォーマンスを改善」「LCP/FID/CLS を最適化」「ウェブサイトを高速化」と依頼した場合
ワークフロー:
-
最大コンテンツ描画(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); -
初回入力遅延(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)); }); } -
累積レイアウトシフト(CLS)を最小化
/* Reserve space for dynamic content */ .product-image-container { aspect-ratio: 1/1; contain: layout; }
アクセシビリティ(WCAG 準拠)
コマンド:アクセシビリティの実装
トリガー:ユーザーが「ARIA を追加」「アクセシビリティに対応」「WCAG に準拠」と依頼した場合
ワークフロー:
-
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> -
キーボードナビゲーション
class KeyboardNavigationManager { handleTab(event) { if (event.shiftKey) { // Backward navigation } else { // Forward navigation } } trapFocus() { // Trap focus within modal } } -
スクリーンリーダーのサポート
class ScreenReaderAnnouncer { announce(message, priority = 'polite') { this.liveRegion.setAttribute('aria-live', priority); this.liveRegion.textContent = message; } }
リアルタイム機能
コマンド:WebSocket サポートの追加
トリガー:ユーザーが「リアルタイム機能を追加」「WebSocket を実装」「ライブ更新」と依頼した場合
ワークフロー:
-
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)); } } } -
サーバー送信イベント(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
リソース
- Odoo ドキュメント:https://www.odoo.com/documentation/
- Bootstrap 5 ドキュメント:https://getbootstrap.com/docs/5.1/
- Owl フレームワーク:https://github.com/odoo/owl
- TypeScript:https://www.typescriptlang.org/docs/
- Jest:https://jestjs.io/docs/
- Cypress:https://docs.cypress.io/
- Web コンポーネント:https://developer.mozilla.org/en-US/docs/Web/Web_Components
- コミュニティフォーラム:https://www.odoo.com/forum/
テーマ機能の有効化システム(重要)
概要: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 で動的ページに利用できるすべてのテンプレートについて説明します。このリファレンスは、次のような場合にお使いください。
- Figma のデザインと比較して、最も近い Odoo テンプレートを見つける
_theme_xxx_post_copy()を使ってテーマのデフォルトを設定する- 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 | セールス 1 | E コマース向けヘッダー | オンラインストア |
website.template_header_sales_two | セールス 2 | カテゴリ付きの E コマース向けヘッダー | 大規模なカタログ |
website.template_header_sales_three | セールス 3 | E コマース向けの別バリエーション | 商品を重視するサイト |
website.template_header_sales_four | セールス 4 | E コマース向けのバリエーション | ファッション、小売 |
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_centerwebsite.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_action | CTA ボタン(お問い合わせ) | 有効 |
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_action | CTA | ニュースレター/アクションを重視 | マーケティングサイト |
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_3 | 4:3 比率 | 無効 | 4:3 の画像アスペクト比 |
website_sale.products_thumb_4_5 | 4:5 比率 | 無効 | 4:5 の画像アスペクト比 |
website_sale.products_thumb_2_3 | 2: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_b2b | B2B フィールド | 無効 | 事業者住所のフィールド |
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_collapse | FAQ | よくある質問 | サポート |
s_tabs | タブ | タブ形式のコンテンツ | 整理 |
動的コンテンツスニペット(15)
| スニペット ID | 名前 | 説明 | 最適な用途 |
|---|---|---|---|
s_google_map | Google マップ | 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_cards | 3つのカード | 3カードレイアウト | サービス |
s_four_cards | 4つのカード | 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_menu | Odooメニュー | 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-class | CSS クラスの切り替え | data-select-class="shadow-lg" |
data-select-style | スタイル編集を有効化 | data-select-style="true" |
data-css-property | 変更する CSS プロパティ | data-css-property="padding" |
data-js | JavaScript ハンドラークラス | 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-15 | Odoo 16-17 | Odoo 18-19 |
|---|---|---|---|
| スニペット数 | 約40 | 81以上 | 155以上 |
| 登録方法 | シンプルな XPath | シンプルな XPath | グループが必須 |
| アセットシステム | テンプレートの継承 | ir.asset モデル | ir.asset モデル |
| JavaScript | odoo.define() | ES6 モジュール | プラグインベース |
| Bootstrap | 4.x | 5.1.3 | 5.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 を指定して動的スニペットを作成する
- 新機能:静的スニペットの完全な一覧(81 個以上のテンプレート)
-
v6.0.0:テーマ機能の有効化システムと動的ページのリファレンス(メジャー)
- 新機能:テーマ機能の有効化システム
theme.utilsモデルの完全なドキュメント_theme_{module}_post_copy()パターンの要件enable_view()およびdisable_view()メソッドのリファレンス- ヘッダー/フッターの相互排他ルール
- 動作する完全な例
- 新機能:動的ページの完全なリファレンス
- XML ID と視覚的な図を含む 11 個のヘッダーテンプレート
- XML ID と視覚的な図を含む 9 個のフッターテンプレート
- すべてのショップページテンプレート(カテゴリー、フィルター、レイアウト)
- すべての商品詳細ページテンプレート
- すべてのブログテンプレート(一覧、詳細、サイドバー)
- カートおよびチェックアウトのテンプレート
- 新機能:デザインワークフローの方法論
- Figma 分析情報の抽出ガイド
- テンプレート選択の判断フローチャート
- デザインに基づく設定の生成
- 手順ごとのワークフロードキュメント
- 更新:
/create-themeコマンド v6.0models/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、スニペット(任意ですが、階層内に存在します)
- 重複するドキュメントのセクションを削除:重複した内容を整理
- $o-theme-font-configs の完全なリファレンス:
-
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 統合を備えた初回リリース