diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts
index 3b65cc69bfe4964fdf1bccc52251cb3a184da27b..0392ec27653e3f75302cc4b3a606520c9b4d3ac9 100644
--- a/src/app/components/generic-calculator/calculator.component.ts
+++ b/src/app/components/generic-calculator/calculator.component.ts
@@ -16,6 +16,7 @@ import { CalculatorNameComponent } from "./calc-name.component";
 import { FormulaireElement } from "../../formulaire/formulaire-element";
 import { FieldsetContainer } from "../../formulaire/fieldset-container";
 import { FieldsetContainerComponent } from "../fieldset-container/fieldset-container.component";
+import { PabTableComponent } from "../pab-table/pab-table.component";
 import { ServiceFactory } from "../../services/service-factory";
 import { MatDialog } from "@angular/material";
 import { DialogConfirmCloseCalcComponent } from "../dialog-confirm-close-calc/dialog-confirm-close-calc.component";
@@ -41,6 +42,12 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
     @ViewChildren(FieldsetContainerComponent)
     private _fieldsetContainerComponents: QueryList<FieldsetContainerComponent>;
 
+    /**
+     * PabTableComponent if any
+     */
+    @ViewChild(PabTableComponent)
+    private _pabTableComponent: PabTableComponent;
+
     /**
      * composant d'affichage des résultats
      */
@@ -336,6 +343,7 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
                 // valeur initiale
                 , this._fieldsetComponents.length > 0);
         }
+
         if (this._fieldsetContainerComponents !== undefined) {
             this._isUIValid = this._isUIValid && this._fieldsetContainerComponents.reduce(
                 // callback
@@ -354,6 +362,10 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
                 // valeur initiale
                 , true);
         }
+
+        if (this._pabTableComponent !== undefined) {
+            this._isUIValid = this._isUIValid && this._pabTableComponent.isValid;
+        }
     }
 
     public getFieldsetStyleDisplay(id: string) {
diff --git a/src/app/components/pab-table/pab-table.component.html b/src/app/components/pab-table/pab-table.component.html
index bdd4f919beb518120e9dec30dfbe784ad3a025d8..ea78638332c644a79a2e3cc3a4d21024e0b26b09 100644
--- a/src/app/components/pab-table/pab-table.component.html
+++ b/src/app/components/pab-table/pab-table.component.html
@@ -74,10 +74,12 @@
                   [ngClass]="cell.class"
                   [class.editable-cell]="hasModel(cell)" [class.readonly-cell]="! hasModel(cell)"
                   [class.selectable-cell]="isSelectable(cell)" [class.selected-cell]="isSelected(cell) && ! isSelected(row)"
+                  [class.invalid-cell]="isInvalid(cell)"
                   [attr.rowspan]="rowSpan(cell)" [attr.colspan]="colSpan(cell)"
                   [title]="cellTitle(cell)">
 
-                    <input matInput *ngIf="isNumberInput(cell)" type="number" [(ngModel)]="cell.model.singleValue">
+                    <input matInput *ngIf="isNumberInput(cell)" step="0.00000000000001" type="number"
+                      [(ngModel)]="cell.model.singleValue" (input)="inputValueChanged($event, cell)">
 
                     <mat-select *ngIf="isSelect(cell)" [value]="cell.modelValue"
                       (selectionChange)="loiDebitSelected($event, cell)">
diff --git a/src/app/components/pab-table/pab-table.component.scss b/src/app/components/pab-table/pab-table.component.scss
index d09c4cad00a211c74ae6cdfed71204c9b04836dc..d8beccf903f460dc26b4cca119a69a3fc7552542 100644
--- a/src/app/components/pab-table/pab-table.component.scss
+++ b/src/app/components/pab-table/pab-table.component.scss
@@ -42,6 +42,9 @@ mat-card-content {
 .selected-editable-cell-bg {
     @extend .bg-accent-verylight;
 }
+.invalid-cell-bg {
+    @extend .bg-error;
+}
 
 #pab-table-toolbar {
     #edit-pab-table {
@@ -117,6 +120,9 @@ mat-card-content {
                                 @extend .selected-editable-cell-bg;
                             }
                         }
+                        &.invalid-cell {
+                            @extend .invalid-cell-bg;
+                        }
                         &.basin_number {
                             text-align: center;
                             font-weight: bold;
diff --git a/src/app/components/pab-table/pab-table.component.ts b/src/app/components/pab-table/pab-table.component.ts
index 6ba74e5623f7215f7afa599b75aab8ee82168d2a..ad5d0c01ee765eaeb166f5cf393d0f86ff45bb44 100644
--- a/src/app/components/pab-table/pab-table.component.ts
+++ b/src/app/components/pab-table/pab-table.component.ts
@@ -1,4 +1,4 @@
-import { Component, Input, Output, EventEmitter, OnInit } from "@angular/core";
+import { Component, Input, Output, EventEmitter, OnInit, AfterViewInit } from "@angular/core";
 
 import { MatDialog } from "@angular/material";
 
@@ -11,7 +11,7 @@ import {
     Nub,
     Structure,
     ParallelStructure,
-    LoiDebit
+    ParamDefinition
  } from "jalhyd";
 
  import { sprintf } from "sprintf-js";
@@ -33,7 +33,7 @@ import { DialogEditPabComponent } from "../dialog-edit-pab/dialog-edit-pab.compo
         "./pab-table.component.scss"
     ]
 })
-export class PabTableComponent implements /* DoCheck, AfterViewInit, */ OnInit {
+export class PabTableComponent implements AfterViewInit, OnInit {
 
     @Input()
     private pabTable: PabTable;
@@ -84,6 +84,7 @@ export class PabTableComponent implements /* DoCheck, AfterViewInit, */ OnInit {
         return this.i18nService.localizeText("INFO_PAB_TABLE");
     }
 
+    /** Global Pab validity */
     public get isValid() {
         return this._isValid;
     }
@@ -139,6 +140,34 @@ export class PabTableComponent implements /* DoCheck, AfterViewInit, */ OnInit {
         return undefined;
     }
 
+    /**
+     * Checks that input value is a valid number, according to input[type="number"] algorithm,
+     * and stores it in cell.uiValidity, so that the <td> element can access it and get angry
+     * if input is invalid
+     */
+    public inputValueChanged($event, cell) {
+        // console.log("input value changed", $event.target.validity.valid, $event.target.validity);
+        if ($event && $event.target && $event.target.validity) {
+            cell.uiValidity = $event.target.validity.valid;
+        }
+        this.updateValidity();
+    }
+
+    /**
+     * Returns true if current cell is bound to a model that says its input value is
+     * no valid, or if characters typed in the input field are not a valid number
+     * (read from cell.uiValidity, see inputValueChanged() above)
+     */
+    public isInvalid(cell: any): boolean {
+        let valid = true;
+        if (this.hasModel(cell) && cell.model instanceof ParamDefinition) {
+            valid = valid && cell.model.isValid;
+        }
+        if (cell.uiValidity !== undefined) {
+            valid = valid && cell.uiValidity;
+        }
+        return ! valid;
+    }
 
     /** returns true if the cell / row has a selectable item */
     public isSelectable(cellOrRow: any): boolean {
@@ -377,7 +406,6 @@ export class PabTableComponent implements /* DoCheck, AfterViewInit, */ OnInit {
             });
             // as much rows as the greatest number of parameters among its devices
             const maxNbParams = this.findMaxNumberOfDeviceParameters(cloison);
-            // console.log(">>> max nb params: ", maxNbParams);
             for (let i = 0; i < maxNbParams; i++) {
                 // build device params row
                 const deviceParamRow = { selectable: cloison, cells: [] };
@@ -585,6 +613,8 @@ export class PabTableComponent implements /* DoCheck, AfterViewInit, */ OnInit {
             // done !
             this.rows.push(deviceParamRowDW);
         }
+
+        this.updateValidity();
     }
 
     private findMaxNumberOfDevices(): number {
@@ -1026,40 +1056,20 @@ export class PabTableComponent implements /* DoCheck, AfterViewInit, */ OnInit {
         }
     }
 
-    /* public ngAfterViewInit() {
-        this.onFieldsetListChange();
-        this._fieldsetComponents.changes.subscribe(_ => this.onFieldsetListChange());
-    }
-
-    public ngDoCheck() {
+    public ngAfterViewInit() {
         this.updateValidity();
-    } */
+    }
 
     /**
-     * @TODO Calcul de la validité de la Pab
+     * Computes the global Pab validity : validity of every cell of every row
      */
     private updateValidity() {
-        this._isValid = false;
-
-        /* if (this._fieldsetComponents !== undefined) {
-            this._isValid = this._fieldsetComponents.reduce(
-                // callback
-                (
-                    // accumulator (valeur précédente du résultat)
-                    acc,
-                    // currentValue (élément courant dans le tableau)
-                    fieldset,
-                    // currentIndex (indice courant dans le tableau)
-                    currIndex,
-                    // array (tableau parcouru)
-                    array
-                ) => {
-                    return acc && fieldset.isValid;
-                }
-                // valeur initiale
-                , this._fieldsetComponents.length > 0);
-        } */
-
+        this._isValid = true;
+        for (const r of this.rows) {
+            for (const c of r.cells) {
+                this._isValid = this._isValid && ! this.isInvalid(c);
+            }
+        }
         this.validChange.emit();
     }
 
diff --git a/src/theme.scss b/src/theme.scss
index f450539546687c9a88b14eab1da37f0f641c919a..951487e7019af1ec6232938437127c43e6907e13 100644
--- a/src/theme.scss
+++ b/src/theme.scss
@@ -171,6 +171,7 @@ $mat-irstea-rouille: (
 $nghyd-primary: mat-palette($mat-irstea-marine);
 $nghyd-accent:  mat-palette($mat-irstea-ocean);
 $nghyd-warn:    mat-palette($mat-irstea-prune);
+$nghyd-error:   mat-palette($mat-irstea-rouille);
 // $nghyd-warn:    mat-palette($mat-irstea-rouille);
 
 $nghyd-theme: mat-light-theme($nghyd-primary, $nghyd-accent, $nghyd-warn);
@@ -181,6 +182,7 @@ $nghyd-theme: mat-light-theme($nghyd-primary, $nghyd-accent, $nghyd-warn);
 $primary: map-get($nghyd-theme, primary);
 $accent: map-get($nghyd-theme, accent);
 $warn: map-get($nghyd-theme, warn);
+// $error: map-get($nghyd-error);
 
 // convenience classes (functions mat-* cannot be used outside of this file)
 
@@ -241,6 +243,13 @@ $warn: map-get($nghyd-theme, warn);
     background-color: mat-color($warn, 50);
 }
 
+.color-error {
+    color: mat-color($nghyd-error);
+}
+.bg-error {
+    background-color: mat-color($nghyd-error);
+}
+
 .mat-button-toggle-checked {
     background-color: mat-color($accent);
     color: mat-color($accent, default-contrast) !important;