diff --git a/web_theme_focus/README.rst b/web_theme_focus/README.rst new file mode 100644 index 000000000000..be818b1b7ef7 --- /dev/null +++ b/web_theme_focus/README.rst @@ -0,0 +1,105 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +============= +Web Theme Focus +============= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:35fa12694771a295a3f9c38e345df0d789738eeee0a8656de912b5a16c1ce656 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github + :target: https://github.com/OCA/web/tree/19.0/web_theme_focus + :alt: OCA/web +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/web-19-0/web-19-0-web_theme_focus + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/web&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Adds a Focus selectable backend theme for the app menu. + +It extends web_responsive's app-menu theme picker with a "Focus" +option that applies the Focus app-switcher gradient background +(light and dark) and makes the top navbar transparent on the home-menu +screen, so the gradient is full-bleed. The module does not aim to fully +replicate Odoo Enterprise; it modernizes the webclient appearance. + +The light background reuses ``web_responsive``'s Milk theme overlay +(OCA/web, LGPL-3); the dark variant uses a darker palette. + +**Table of contents** + +.. contents:: + :local: + +Known issues / Roadmap +====================== + +Known issues and future enhancements: + +- Brand/surface color overrides (light + dark) for the Focus theme. +- Additional modern design tweaks. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* volkantasci + +Contributors +------------ + +- volkantasci https://github.com/volkantasci + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +.. |maintainer-volkantasci| image:: https://github.com/volkantasci.png?size=40px + :target: https://github.com/volkantasci + :alt: volkantasci + +Current `maintainer `__: + +|maintainer-volkantasci| + +This module is part of the `OCA/web `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/web_theme_focus/__init__.py b/web_theme_focus/__init__.py new file mode 100644 index 000000000000..0650744f6bc6 --- /dev/null +++ b/web_theme_focus/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/web_theme_focus/__manifest__.py b/web_theme_focus/__manifest__.py new file mode 100644 index 000000000000..5d8d343af88a --- /dev/null +++ b/web_theme_focus/__manifest__.py @@ -0,0 +1,26 @@ +# Copyright 2026 volkantasci +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Web Theme Focus", + "summary": "Focus theme - larger icons, hidden search, transparent navbar", + "license": "AGPL-3", + "version": "19.0.1.0.0", + "website": "https://github.com/OCA/web", + "author": "volkantasci, Odoo Community Association (OCA)", + "maintainers": ["volkantasci"], + "category": "Extra Tools", + "depends": ["web", "web_dark_mode", "web_responsive"], + "excludes": ["web_enterprise"], + "installable": True, + "assets": { + "web.assets_backend": [ + "web_theme_focus/static/src/js/app_theme.esm.js", + "web_theme_focus/static/src/scss/apps_menu.scss", + "web_theme_focus/static/src/scss/navbar.scss", + ], + "web.assets_web_dark": [ + "web_theme_focus/static/src/**/*.dark.scss", + ], + }, +} diff --git a/web_theme_focus/i18n/tr.po b/web_theme_focus/i18n/tr.po new file mode 100644 index 000000000000..5d9b4e755ffa --- /dev/null +++ b/web_theme_focus/i18n/tr.po @@ -0,0 +1,21 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * web_theme_focus +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 19.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2026-09-01 12:00+0000\n" +"Last-Translator: volkantasci\n" +"Language-Team: \n" +"Language: tr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" + +#. module: web_theme_focus +#: model:ir.model.fields.selection,name:web_theme_focus.selection__res_users__apps_menu_theme__focus +msgid "Focus" +msgstr "Odak" diff --git a/web_theme_focus/i18n/web_theme_focus.pot b/web_theme_focus/i18n/web_theme_focus.pot new file mode 100644 index 000000000000..8bdc1355dc09 --- /dev/null +++ b/web_theme_focus/i18n/web_theme_focus.pot @@ -0,0 +1,19 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * web_theme_focus +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 19.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: web_theme_focus +#: model:ir.model.fields.selection,name:web_theme_focus.selection__res_users__apps_menu_theme__focus +msgid "Focus" +msgstr "" diff --git a/web_theme_focus/models/__init__.py b/web_theme_focus/models/__init__.py new file mode 100644 index 000000000000..8835165330f8 --- /dev/null +++ b/web_theme_focus/models/__init__.py @@ -0,0 +1 @@ +from . import res_users diff --git a/web_theme_focus/models/res_users.py b/web_theme_focus/models/res_users.py new file mode 100644 index 000000000000..9701966ce286 --- /dev/null +++ b/web_theme_focus/models/res_users.py @@ -0,0 +1,13 @@ +# Copyright 2026 volkantasci +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResUsers(models.Model): + _inherit = "res.users" + + apps_menu_theme = fields.Selection( + selection_add=[("focus", "Focus")], + ondelete={"focus": "cascade"}, + ) diff --git a/web_theme_focus/pyproject.toml b/web_theme_focus/pyproject.toml new file mode 100644 index 000000000000..4231d0cccb3d --- /dev/null +++ b/web_theme_focus/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/web_theme_focus/readme/CONTRIBUTORS.md b/web_theme_focus/readme/CONTRIBUTORS.md new file mode 100644 index 000000000000..28146e14fdcd --- /dev/null +++ b/web_theme_focus/readme/CONTRIBUTORS.md @@ -0,0 +1 @@ +- volkantasci diff --git a/web_theme_focus/readme/DESCRIPTION.md b/web_theme_focus/readme/DESCRIPTION.md new file mode 100644 index 000000000000..e76dace11df2 --- /dev/null +++ b/web_theme_focus/readme/DESCRIPTION.md @@ -0,0 +1,8 @@ +Focus backend theme for the app menu. + +Extends web_responsive's app-menu theme picker with a "Focus" option that +provides a minimal, distraction-free home screen for deployments with few +apps: it hides the search bar, enlarges app icons with more spacing, and +makes the top navbar transparent on the home-menu screen so the gradient +background is full-bleed (light and dark variants). It keeps regular views +unchanged and does not replicate Odoo Enterprise. diff --git a/web_theme_focus/readme/ROADMAP.md b/web_theme_focus/readme/ROADMAP.md new file mode 100644 index 000000000000..5d51d1432291 --- /dev/null +++ b/web_theme_focus/readme/ROADMAP.md @@ -0,0 +1,4 @@ +Known issues and future enhancements: + +- Brand/surface color overrides (light + dark) for the Focus theme. +- Additional modern design tweaks. diff --git a/web_theme_focus/static/description/icon.png b/web_theme_focus/static/description/icon.png new file mode 100644 index 000000000000..1dcc49c24f36 Binary files /dev/null and b/web_theme_focus/static/description/icon.png differ diff --git a/web_theme_focus/static/description/index.html b/web_theme_focus/static/description/index.html new file mode 100644 index 000000000000..d06dbbda87b5 --- /dev/null +++ b/web_theme_focus/static/description/index.html @@ -0,0 +1,12 @@ +
+
+

Web Theme Focus

+

Minimal focus backend theme

+

+ Extends web_responsive's app-menu theme picker with a Focus option: + hides the search bar, enlarges app icons with more spacing, and + makes the top navbar transparent for a full-bleed gradient + background (light and dark variants). +

+
+
diff --git a/web_theme_focus/static/src/img/app_switcher_background_light.svg b/web_theme_focus/static/src/img/app_switcher_background_light.svg new file mode 100644 index 000000000000..711d549ef7eb --- /dev/null +++ b/web_theme_focus/static/src/img/app_switcher_background_light.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/web_theme_focus/static/src/js/app_theme.esm.js b/web_theme_focus/static/src/js/app_theme.esm.js new file mode 100644 index 000000000000..d624fbcbb6bc --- /dev/null +++ b/web_theme_focus/static/src/js/app_theme.esm.js @@ -0,0 +1,20 @@ +/* Copyright 2026 volkantasci + * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */ + +import {WebClient} from "@web/webclient/webclient"; +import {patch} from "@web/core/utils/patch"; +import {session} from "@web/session"; + +// Mirror web_responsive's active app-menu theme onto so CSS can reach +// elements outside .o_grid_apps_menu (e.g. the navbar). Odoo 19's patch util +// does not provide this._super — call super.setup(...arguments). +// +// Falls back to "milk" (web_responsive's default theme) if the session info +// carries no apps_menu theme; web_responsive is a hard dependency, so this +// only guards against an unexpectedly empty payload. +patch(WebClient.prototype, { + setup() { + super.setup(...arguments); + document.body.dataset.appTheme = session.apps_menu?.theme || "milk"; + }, +}); diff --git a/web_theme_focus/static/src/scss/apps_menu.dark.scss b/web_theme_focus/static/src/scss/apps_menu.dark.scss new file mode 100644 index 000000000000..d4bde072b985 --- /dev/null +++ b/web_theme_focus/static/src/scss/apps_menu.dark.scss @@ -0,0 +1,10 @@ +// Copyright 2026 volkantasci +// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +/* web_theme_focus — app-menu color variables (dark). */ +body[data-app-theme="focus"] { + --app-menu-background: linear-gradient(135deg, #1d1e23, #101114); + --app-menu-text-color: #c8c8c8; + --app-menu-hover-background: rgba(255, 255, 255, 0.08); + --app-menu-text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.4); +} diff --git a/web_theme_focus/static/src/scss/apps_menu.scss b/web_theme_focus/static/src/scss/apps_menu.scss new file mode 100644 index 000000000000..9f4b0150ba45 --- /dev/null +++ b/web_theme_focus/static/src/scss/apps_menu.scss @@ -0,0 +1,37 @@ +// Copyright 2026 volkantasci +// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +/* web_theme_focus — app-menu color variables (light). + Defined on (set by app_theme.esm.js) so the full-bleed gradient + painted on .o_web_client (see navbar.scss) can read --app-menu-background. */ +body[data-app-theme="focus"] { + --app-menu-background: + url("../img/app_switcher_background_light.svg"), + linear-gradient(to bottom, #e9e6f9, #edebf8); + --app-menu-text-color: #3c3c3c; + --app-menu-text-shadow: 1px 1px 1px rgba(255, 255, 255, 0.4); + --app-menu-hover-background: rgba(255, 255, 255, 0.35); +} + +/* Focus app drawer: no search bar, larger app + icons and more spacing between them. Applies to both color schemes. */ +.o_grid_apps_menu[data-theme="focus"] { + .search-container { + display: none !important; + } + + .o-app-menu-list { + grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); + gap: 0.5rem; + } + + .o-app-menu-item { + padding: 1rem 0.75rem; + gap: 0.5rem; + } + + .o-app-menu-item__icon { + width: 70px; + max-width: 70px; + } +} diff --git a/web_theme_focus/static/src/scss/navbar.dark.scss b/web_theme_focus/static/src/scss/navbar.dark.scss new file mode 100644 index 000000000000..8c872aaea444 --- /dev/null +++ b/web_theme_focus/static/src/scss/navbar.dark.scss @@ -0,0 +1,24 @@ +// Copyright 2026 volkantasci +// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +/* web_theme_focus — dark variant of the full-bleed Focus gradient and the + navbar content color. Loaded via the web.assets_web_dark bundle (dark color + scheme only): overrides the light background-image, and restores the light + (white) navbar content so it stays readable over the dark gradient. */ +body[data-app-theme="focus"].o_apps_menu_opened { + background-image: linear-gradient(135deg, #1d1e23, #101114); + + .o_main_navbar { + color: rgba(255, 255, 255, 0.9); + --NavBar-entry-color: rgba(255, 255, 255, 0.9); + --NavBar-entry-color--hover: #ffffff; + --NavBar-entry-color--active: #ffffff; + --NavBar-brand-color: rgba(255, 255, 255, 0.9); + --NavBar-entry-backgroundColor--hover: rgba(255, 255, 255, 0.08); + --NavBar-entry-backgroundColor--active: rgba(255, 255, 255, 0.08); + } + + .o_grid_apps_menu__button { + color: rgba(255, 255, 255, 0.9) !important; + } +} diff --git a/web_theme_focus/static/src/scss/navbar.scss b/web_theme_focus/static/src/scss/navbar.scss new file mode 100644 index 000000000000..6159c4f5b087 --- /dev/null +++ b/web_theme_focus/static/src/scss/navbar.scss @@ -0,0 +1,56 @@ +// Copyright 2026 volkantasci +// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +/* web_theme_focus — full-bleed Focus gradient behind a transparent navbar. + In Odoo 19 the webclient root IS (body_classname = "o_web_client"), + so the gradient must be painted on body itself, not on a descendant + .o_web_client. web_responsive's .app-menu-container starts below the navbar + (it is a navbar descendant), so its gradient never reaches behind the + navbar. Paint the gradient on body, keep the navbar and the app-menu + container transparent, so one continuous gradient shows through. The + linear-gradient layer is a fallback: even if the SVG cannot be fetched, + the drawer is never blank. + + Light mode: the navbar is transparent over the light gradient, so its + content (normally white, designed for the dark navbar) is re-colored dark + for contrast. Dark mode restores the white content in navbar.dark.scss. */ +body[data-app-theme="focus"].o_apps_menu_opened { + background-image: + url("../img/app_switcher_background_light.svg"), + linear-gradient(to bottom, #e9e6f9, #edebf8); + background-size: cover; + background-position: center; + background-repeat: no-repeat; + + .o_main_navbar { + background: transparent !important; + border-bottom: none !important; + box-shadow: none !important; + color: #3c3c3c; + --NavBar-entry-color: #3c3c3c; + --NavBar-entry-color--hover: #1a1a1a; + --NavBar-entry-color--active: #1a1a1a; + --NavBar-brand-color: #3c3c3c; + --NavBar-entry-backgroundColor--hover: rgba(0, 0, 0, 0.06); + --NavBar-entry-backgroundColor--active: rgba(0, 0, 0, 0.06); + } + + .o_grid_apps_menu__button { + color: #3c3c3c !important; + } + + /* The transparent drawer container would let the previously-opened view + show through behind the gradient — hide the action area while the + drawer is open (the full-bleed body gradient takes its place). + visibility alone is not enough: descendants that force + visibility: visible (e.g. kanban card kebab buttons on hover, field + translate buttons) stay visible. opacity: 0 hides the whole subtree. */ + .o_action_manager { + opacity: 0; + visibility: hidden; + } + + .app-menu-container { + background: transparent !important; + } +} diff --git a/web_theme_focus/tests/__init__.py b/web_theme_focus/tests/__init__.py new file mode 100644 index 000000000000..675ae6b3737d --- /dev/null +++ b/web_theme_focus/tests/__init__.py @@ -0,0 +1 @@ +from . import test_apps_menu_theme diff --git a/web_theme_focus/tests/test_apps_menu_theme.py b/web_theme_focus/tests/test_apps_menu_theme.py new file mode 100644 index 000000000000..a950dafa580d --- /dev/null +++ b/web_theme_focus/tests/test_apps_menu_theme.py @@ -0,0 +1,68 @@ +# Copyright 2026 volkantasci +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import json + +from odoo import Command +from odoo.tests import HttpCase, TransactionCase, tagged + + +class TestAppsMenuTheme(TransactionCase): + def test_selection_includes_focus(self): + selection = self.env["res.users"].fields_get(["apps_menu_theme"])[ + "apps_menu_theme" + ]["selection"] + keys = [key for key, _label in selection] + self.assertIn("focus", keys) + + def test_user_can_be_set_to_focus(self): + user = self.env["res.users"].create( + { + "name": "Focus Theme Test", + "login": "focus_theme_test", + "apps_menu_theme": "focus", + } + ) + self.assertEqual(user.apps_menu_theme, "focus") + + def test_dark_bundle_registers_focus_assets(self): + """The focus dark SCSS files must be part of web.assets_web_dark.""" + asset_paths = self.env["ir.asset"]._get_asset_paths( + "web.assets_web_dark", self.env["ir.asset"]._get_asset_params() + ) + paths = [path for path, *_rest in asset_paths] + self.assertIn("/web_theme_focus/static/src/scss/apps_menu.dark.scss", paths) + self.assertIn("/web_theme_focus/static/src/scss/navbar.dark.scss", paths) + + +@tagged("-at_install", "post_install") +class TestWebclientAssetBundle(HttpCase): + def test_webclient_bootstrap_renders(self): + self.authenticate("admin", "admin") + response = self.url_open("/odoo") + self.assertEqual(response.status_code, 200, response.text[:500]) + + +@tagged("-at_install", "post_install") +class TestSessionInfoTheme(HttpCase): + def test_session_info_reports_focus_theme(self): + self.env["res.users"].create( + { + "name": "Focus Theme Session", + "login": "focus_theme_session", + "password": "focus_theme_session", + "group_ids": [Command.link(self.env.ref("base.group_user").id)], + "apps_menu_theme": "focus", + } + ) + self.authenticate("focus_theme_session", "focus_theme_session") + response = self.url_open("/web") + self.assertEqual(response.status_code, 200, response.text[:500]) + session_info = None + key = "odoo.__session_info__ = " + for line in response.text.splitlines(): + if key in line: + session_info = json.loads(line.split(key, 1)[1].rstrip(";")) + break + self.assertIsNotNone(session_info, "session info not found in /web page") + self.assertEqual(session_info["apps_menu"]["theme"], "focus")