diff --git a/addons/website/models/theme_models.py b/addons/website/models/theme_models.py
index 4f50869301d..7ea51e50ce5 100644
--- a/addons/website/models/theme_models.py
+++ b/addons/website/models/theme_models.py
@@ -172,11 +172,15 @@ class Theme(models.AbstractModel):
'navbar-font': 'null',
'buttons-font': 'null',
'color-palettes-number': 'null',
+ 'btn-ripple': 'null',
'header-template': 'null',
'footer-template': 'null',
}
)
+ # Reinitialize effets
+ self.disable_view('website.option_ripple_effect')
+
# Reinitialize header templates
self.disable_view('website.template_header_hamburger')
self.disable_view('website.template_header_hamburger_left')
diff --git a/addons/website/static/src/js/content/ripple_effect.js b/addons/website/static/src/js/content/ripple_effect.js
new file mode 100644
index 00000000000..2e61d5b7cd8
--- /dev/null
+++ b/addons/website/static/src/js/content/ripple_effect.js
@@ -0,0 +1,72 @@
+odoo.define('website.ripple_effect', function (require) {
+'use strict';
+
+const publicWidget = require('web.public.widget');
+
+publicWidget.registry.RippleEffect = publicWidget.Widget.extend({
+ selector: '.btn, .dropdown-toggle, .dropdown-item',
+ events: {
+ 'click': '_onClick',
+ },
+ duration: 350,
+
+ /**
+ * @override
+ */
+ start: async function () {
+ this.diameter = Math.max(this.$el.outerWidth(), this.$el.outerHeight());
+ this.offsetX = this.$el.offset().left;
+ this.offsetY = this.$el.offset().top;
+ return this._super(...arguments);
+ },
+ /**
+ * @override
+ */
+ destroy: function () {
+ this._super(...arguments);
+ if (this.rippleEl) {
+ this.rippleEl.remove();
+ }
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ * @param {boolean} [toggle]
+ */
+ _toggleRippleEffect: function (toggle) {
+ this.el.classList.toggle('o_js_ripple_effect', toggle);
+ },
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ * @param {Event} ev
+ */
+ _onClick: function (ev) {
+ if (!this.rippleEl) {
+ this.rippleEl = document.createElement('span');
+ this.rippleEl.classList.add('o_ripple_item');
+ this.rippleEl.style.animationDuration = `${this.duration}ms`;
+ this.rippleEl.style.width = `${this.diameter}px`;
+ this.rippleEl.style.height = `${this.diameter}px`;
+ this.el.appendChild(this.rippleEl);
+ }
+
+ clearTimeout(this.timeoutID);
+ this._toggleRippleEffect(false);
+
+ this.rippleEl.style.top = `${ev.pageY - this.offsetY - this.diameter / 2}px`;
+ this.rippleEl.style.left = `${ev.pageX - this.offsetX - this.diameter / 2}px`;
+
+ this._toggleRippleEffect(true);
+ this.timeoutID = setTimeout(() => this._toggleRippleEffect(false), this.duration);
+ },
+});
+});
diff --git a/addons/website/static/src/scss/options/ripple_effect.scss b/addons/website/static/src/scss/options/ripple_effect.scss
new file mode 100644
index 00000000000..cff35300d55
--- /dev/null
+++ b/addons/website/static/src/scss/options/ripple_effect.scss
@@ -0,0 +1,28 @@
+@keyframes o-btn-ripple {
+ 100% {
+ opacity: 0;
+ transform: scale(2.5);
+ }
+}
+
+.o_ripple_item {
+ display: none;
+ position: absolute;
+ z-index: -1;
+ border-radius: 100%;
+ opacity: .3;
+ background: currentColor;
+ pointer-events: none;
+ transform: scale(0);
+}
+
+.o_js_ripple_effect {
+ transform-style: preserve-3d;
+ position: relative !important;
+ overflow: hidden !important;
+
+ .o_ripple_item {
+ display: block;
+ animation: o-btn-ripple ease-in;
+ }
+}
diff --git a/addons/website/static/src/scss/primary_variables.scss b/addons/website/static/src/scss/primary_variables.scss
index c4c6d80a446..044f2629cf2 100644
--- a/addons/website/static/src/scss/primary_variables.scss
+++ b/addons/website/static/src/scss/primary_variables.scss
@@ -252,6 +252,7 @@ $o-base-website-values-palette: (
'btn-primary-outline': false,
'btn-secondary-outline': false,
'link-underline': 'hover', // 'never' / 'hover' / 'always'
+ 'btn-ripple': false,
// A key from the $o-theme-font-configs map (null = default to the first key)
'font': null,
diff --git a/addons/website/views/snippets/snippets.xml b/addons/website/views/snippets/snippets.xml
index d49d03291e8..1da86d618d6 100644
--- a/addons/website/views/snippets/snippets.xml
+++ b/addons/website/views/snippets/snippets.xml
@@ -916,6 +916,10 @@
Fill
Outline
+
Normal
Underline On Hover
diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml
index 94685591390..c65f65ca386 100644
--- a/addons/website/views/website_templates.xml
+++ b/addons/website/views/website_templates.xml
@@ -721,6 +721,16 @@
+
+
+
+
+
+
+
+
+
+