diff --git a/addons/web/static/src/js/views/list/list_editable_renderer.js b/addons/web/static/src/js/views/list/list_editable_renderer.js
index 697430e24b769a69f5897422c4e3787bb5d3bd2b..38a96f04bcaf7c5d34ae46b5a05cd3edfd58dae9 100644
--- a/addons/web/static/src/js/views/list/list_editable_renderer.js
+++ b/addons/web/static/src/js/views/list/list_editable_renderer.js
@@ -186,7 +186,7 @@ ListRenderer.include({
             if (widgets.length) {
                 var $row = self._getRow(recordID);
                 var record = self._getRecord(recordID);
-                self._setDecorationClasses(record, $row);
+                self._setDecorationClasses($row, self.rowDecorations, record);
                 self._updateFooter();
             }
             return widgets;
diff --git a/addons/web/static/src/js/views/list/list_renderer.js b/addons/web/static/src/js/views/list/list_renderer.js
index 13deb446c1cd55660a6edfff3ec2b3cc291b14e1..93576be93489804b2b5b13709f8d2bac7a9aac3f 100644
--- a/addons/web/static/src/js/views/list/list_renderer.js
+++ b/addons/web/static/src/js/views/list/list_renderer.js
@@ -58,12 +58,12 @@ var ListRenderer = BasicRenderer.extend({
     init: function (parent, state, params) {
         this._super.apply(this, arguments);
         this.columnInvisibleFields = params.columnInvisibleFields;
-        this.rowDecorations = _.chain(this.arch.attrs)
-            .pick(function (value, key) {
-                return DECORATIONS.indexOf(key) >= 0;
-            }).mapObject(function (value) {
-                return py.parse(py.tokenize(value));
-            }).value();
+        this.rowDecorations = this._extractDecorationAttrs(this.arch);
+        this.fieldDecorations = {};
+        for (const field of this.arch.children.filter(c => c.tag === 'field')) {
+            const decorations = this._extractDecorationAttrs(field);
+            this.fieldDecorations[field.attrs.name] = decorations;
+        }
         this.hasSelectors = params.hasSelectors;
         this.selection = params.selectedRecords || [];
         this.pagers = []; // instantiated pagers (only for grouped lists)
@@ -186,6 +186,24 @@ var ListRenderer = BasicRenderer.extend({
             };
         }
     },
+    /**
+     * Extract the decoration attributes (e.g. decoration-danger) of a node. The
+     * condition is processed such that it is ready to be evaluated.
+     *
+     * @private
+     * @param {Object} node the <tree> or a <field> node
+     * @returns {Object}
+     */
+    _extractDecorationAttrs: function (node) {
+        const decorations = {};
+        for (const [key, expr] of Object.entries(node.attrs)) {
+            if (DECORATIONS.includes(key)) {
+                const cssClass = key.replace('decoration', 'text');
+                decorations[cssClass] = py.parse(py.tokenize(expr));
+            }
+        }
+        return decorations;
+    },
     /**
      *
      * @private
@@ -440,6 +458,8 @@ var ListRenderer = BasicRenderer.extend({
             return $td.append($el);
         }
         this._handleAttributes($td, node);
+        this._setDecorationClasses($td, this.fieldDecorations[node.attrs.name], record);
+
         var name = node.attrs.name;
         var field = this.state.fields[name];
         var value = record.data[name];
@@ -474,7 +494,7 @@ var ListRenderer = BasicRenderer.extend({
         if (node.attrs.icon) {
             // if there is an icon, we force the btn-link style, unless a btn-xxx
             // style class is explicitely provided
-            const btnStyleRegex = /\bbtn-(primary|secondary|link|success|info|warning|danger)\b/;
+            const btnStyleRegex = /\bbtn-[a-z]+\b/;
             if (!btnStyleRegex.test(nodeWithoutWidth.attrs.class)) {
                 extraClass = 'btn-link o_icon_button';
             }
@@ -843,7 +863,7 @@ var ListRenderer = BasicRenderer.extend({
         if (this.hasSelectors) {
             $tr.prepend(this._renderSelector('td', !record.res_id));
         }
-        this._setDecorationClasses(record, $tr);
+        this._setDecorationClasses($tr, this.rowDecorations, record);
         return $tr;
     },
     /**
@@ -1006,22 +1026,24 @@ var ListRenderer = BasicRenderer.extend({
         return Promise.all([this._super.apply(this, arguments), prom]);
     },
     /**
-     * Each line can be decorated according to a few simple rules. The arch
-     * description of the list may have one of the decoration-X attribute with
-     * a domain as value.  Then, for each record, we check if the domain matches
-     * the record, and add the text-X css class to the element.  This method is
-     * concerned with the computation of the list of css classes for a given
-     * record.
+     * Each line or cell can be decorated according to a few simple rules. The
+     * arch description of the list or the field nodes may have one of the
+     * decoration-X attributes with a python expression as value. Then, for each
+     * record, we evaluate the python expression, and conditionnaly add the
+     * text-X css class to the element.  This method is concerned with the
+     * computation of the list of css classes for a given record.
      *
      * @private
+     * @param {jQueryElement} $el the element to which to add the classes (a tr
+     *   or td)
+     * @param {Object} decorations keys are the decoration classes (e.g.
+     *   'text-bf') and values are the python expressions to evaluate
      * @param {Object} record a basic model record
-     * @param {jQueryElement} $tr a jquery <tr> element (the row to add decoration)
      */
-    _setDecorationClasses: function (record, $tr) {
-        _.each(this.rowDecorations, function (expr, decoration) {
-            var cssClass = decoration.replace('decoration', 'text');
-            $tr.toggleClass(cssClass, py.PY_isTrue(py.evaluate(expr, record.evalContext)));
-        });
+    _setDecorationClasses: function ($el, decorations, record) {
+        for (const [cssClass, expr] of Object.entries(decorations)) {
+            $el.toggleClass(cssClass, py.PY_isTrue(py.evaluate(expr, record.evalContext)));
+        }
     },
     /**
      * @private
diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js
index 913d75ed8bfd63936a70ca0d9ab5c091c837d63b..03e62488ba40aad2f8cb480754797dc0765ebccc 100644
--- a/addons/web/static/tests/views/list_tests.js
+++ b/addons/web/static/tests/views/list_tests.js
@@ -3356,6 +3356,27 @@ QUnit.module('Views', {
         list.destroy();
     });
 
+    QUnit.test('support field decoration', async function (assert) {
+        assert.expect(3);
+
+        var list = await createView({
+            View: ListView,
+            model: 'foo',
+            data: this.data,
+            arch: `
+                <tree>
+                    <field name="foo" decoration-danger="int_field > 5"/>
+                    <field name="int_field"/>
+                </tree>`,
+        });
+
+        assert.containsN(list, 'tbody tr', 4, "should have 4 rows");
+        assert.containsN(list, 'tbody td.o_list_char.text-danger', 3);
+        assert.containsNone(list, 'tbody td.o_list_number.text-danger');
+
+        list.destroy();
+    });
+
     QUnit.test('no content helper when no data', async function (assert) {
         assert.expect(5);
 
diff --git a/doc/reference/views.rst b/doc/reference/views.rst
index c36327df7aa469b0074f7c2a622c4980ca2fee7d..4dad4a4f72645843c964ce2ce4a24c331a581a1a 100644
--- a/doc/reference/views.rst
+++ b/doc/reference/views.rst
@@ -1469,6 +1469,13 @@ Possible children elements of the list view are:
         be 3 times larger than the others). Note that when there are records in
         the list, we let the browser automatically adapt the column's widths
         according to their content, and this attribute is thus ignored.
+    ``decoration-{$name}``
+        allow changing the style of a cell's text based on the corresponding
+        record's attributes.
+
+        ``{$name}`` can be ``bf`` (``font-weight: bold``), ``it``
+        (``font-style: italic``), or any `bootstrap contextual color`_ (``danger``,
+        ``info``, ``muted``, ``primary``, ``success`` or ``warning``).
 
     .. note::
 
diff --git a/odoo/addons/base/rng/common.rng b/odoo/addons/base/rng/common.rng
index 7ac9f7852d49d7ae3564e84b9bc64d8884bea3ad..a81a6e5761a5ee8af092693555e58d1dd49b9da5 100644
--- a/odoo/addons/base/rng/common.rng
+++ b/odoo/addons/base/rng/common.rng
@@ -264,6 +264,14 @@
             <rng:optional><rng:attribute name="write_field" /></rng:optional>
             <rng:optional><rng:attribute name="text" /></rng:optional>
             <rng:optional><rng:attribute name="optional" /></rng:optional>
+            <rng:optional><rng:attribute name="decoration-bf"/></rng:optional>
+            <rng:optional><rng:attribute name="decoration-it"/></rng:optional>
+            <rng:optional><rng:attribute name="decoration-danger"/></rng:optional>
+            <rng:optional><rng:attribute name="decoration-info"/></rng:optional>
+            <rng:optional><rng:attribute name="decoration-muted"/></rng:optional>
+            <rng:optional><rng:attribute name="decoration-primary"/></rng:optional>
+            <rng:optional><rng:attribute name="decoration-success"/></rng:optional>
+            <rng:optional><rng:attribute name="decoration-warning"/></rng:optional>
             <rng:optional><rng:attribute name="kanban_view_ref" /></rng:optional>
             <rng:optional>
                 <rng:attribute name="force_save">