diff --git a/angular.json b/angular.json
index 1bdcb122c0ed8d56b14fee48d3f60274ba399cde..cb7ab0bee214dd7bc0fa55fc47dc9aa3df74b162 100644
--- a/angular.json
+++ b/angular.json
@@ -25,8 +25,6 @@
               { "glob": "**/*.png", "input": "src/", "output": "/" }
             ],
             "styles": [
-              "node_modules/angular-bootstrap-md/scss/bootstrap/bootstrap.scss",
-              "node_modules/angular-bootstrap-md/scss/mdb-free.scss",
               "src/styles.scss",
               "src/theme.scss"
             ],
@@ -42,7 +40,7 @@
               "aot": true,
               "extractLicenses": true,
               "vendorChunk": false,
-              "buildOptimizer": true,
+              "buildOptimizer": false,
               "fileReplacements": [
                 {
                   "replace": "src/environments/environment.ts",
@@ -79,8 +77,6 @@
             "tsConfig": "src/tsconfig.spec.json",
             "scripts": [],
             "styles": [
-              "node_modules/angular-bootstrap-md/scss/bootstrap/bootstrap.scss",
-              "node_modules/angular-bootstrap-md/scss/mdb-free.scss",
               "src/styles.scss",
               "src/theme.scss"
             ],
diff --git a/package-lock.json b/package-lock.json
index f8fa418718ae8400a1092e9aa7896fb1912de27e..e5839dd8addd49453ffe173ae960ebd23db062b7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1481,11 +1481,6 @@
       "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
       "dev": true
     },
-    "angular-bootstrap-md": {
-      "version": "7.3.0",
-      "resolved": "https://registry.npmjs.org/angular-bootstrap-md/-/angular-bootstrap-md-7.3.0.tgz",
-      "integrity": "sha512-FACXdj+fGe7aA1yNBoFFV6I8Gs9+ithMdGAl4ZJ7DxqD5JudtWqlAwapNpqXzf7r17b9+vIGAAMmVTfcc+i2Dw=="
-    },
     "angular2-chartjs": {
       "version": "0.5.1",
       "resolved": "https://registry.npmjs.org/angular2-chartjs/-/angular2-chartjs-0.5.1.tgz",
@@ -6782,8 +6777,12 @@
       "version": "file:../jalhyd",
 =======
       "version": "file:../jalhyd/jalhyd-1.0.0.tgz",
+<<<<<<< e94eb7b6543d2a58a90ae771c869e558f09f5cf5
       "integrity": "sha512-sT+DU/Uyg2s04TqEHCoM8dKQDvFwjj30ax8MO2EOHa2AOzd9+OHO0z5KbvwAHYadrW4p/kxz8p2SK/KLDsfQ0g==",
 >>>>>>> Màj dépendances
+=======
+      "integrity": "sha512-aB8rBtIjyt5EKGXOWRCarFduWhLpct4gEzIHwxeU2BTQpo043CybEDN7kC2w6NprK/NSfcgZBDC9APdcjnBsig==",
+>>>>>>> [WIP] materialification des modules de calcul
       "requires": {
         "buffer": "^5.2.1"
       },
diff --git a/package.json b/package.json
index 3f6f702194ff3a3ad41ea74ef60779db70541b0f..9d2e430d150f2bc558292d5eaf4b43daed41b19a 100644
--- a/package.json
+++ b/package.json
@@ -32,7 +32,6 @@
     "@angular/platform-browser": "^7.2.1",
     "@angular/platform-browser-dynamic": "^7.2.1",
     "@angular/router": "^7.2.1",
-    "angular-bootstrap-md": "^7.3.0",
     "angular2-chartjs": "^0.5.1",
     "core-js": "^2.6.3",
     "file-saver": "^2.0.0",
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 974987c227ad34fe1e042f34cf5471d2f3ea1b4e..f4473c1a06f2ea991392d3fae37c283af037642d 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -16,12 +16,19 @@ import {
   MatInputModule,
   MatListModule,
   MatCardModule,
-  ErrorStateMatcher
+  ErrorStateMatcher,
+  MatButtonToggleModule
 } from "@angular/material";
 import { MaterialFileInputModule } from "ngx-material-file-input";
 
 import { FlexLayoutModule } from "@angular/flex-layout";
-import { MDBBootstrapModule } from "angular-bootstrap-md";
+import {
+  CustomBreakPointsProvider,
+  FlexGtXxsShowHideDirective,
+  FlexXxsShowHideDirective,
+  FlexLtXsShowHideDirective
+} from "./directives/flex-xxs.directive";
+
 import { HttpClientModule } from "@angular/common/http";
 import { FormsModule, ReactiveFormsModule } from "@angular/forms"; // <-- NgModel lives here
 import { ChartModule } from "angular2-chartjs";
@@ -39,11 +46,7 @@ import { NgParamInputComponent } from "./components/ngparam-input/ngparam-input.
 import { FieldSetComponent } from "./components/field-set/field-set.component";
 import { FieldsetContainerComponent } from "./components/fieldset-container/fieldset-container.component";
 import { ParamFieldLineComponent } from "./components/param-field-line/param-field-line.component";
-import { NgParamMinComponent } from "./components/param-values/ngparam-min.component";
-import { NgParamMaxComponent } from "./components/param-values/ngparam-max.component";
-import { NgParamStepComponent } from "./components/param-values/ngparam-step.component";
 import { ParamValuesComponent } from "./components/param-values/param-values.component";
-import { ValueListComponent } from "./components/param-values/value-list.component";
 import { SelectFieldLineComponent } from "./components/select-field-line/select-field-line.component";
 import { CheckFieldLineComponent } from "./components/check-field-line/check-field-line.component";
 import { CalculatorResultsComponent } from "./components/calculator-results/calculator-results.component";
@@ -70,12 +73,15 @@ import { ParamLinkComponent } from "./components/param-link/param-link.component
 
 import { DialogConfirmEmptySessionComponent } from "./components/dialog-confirm-empty-session/dialog-confirm-empty-session.component";
 import { DialogConfirmCloseCalcComponent } from "./components/dialog-confirm-close-calc/dialog-confirm-close-calc.component";
+import { DialogEditParamComputedComponent } from "./components/dialog-edit-param-computed/dialog-edit-param-computed.component";
+import { DialogEditParamValuesComponent } from "./components/dialog-edit-param-values/dialog-edit-param-values.component";
 import { DialogLoadSessionComponent } from "./components/dialog-load-session/dialog-load-session.component";
 import { DialogSaveSessionComponent } from "./components/dialog-save-session/dialog-save-session.component";
 
 import { JalhydAsyncModelValidationDirective } from "./directives/jalhyd-async-model-validation.directive";
 import { JalhydModelValidationDirective } from "./directives/jalhyd-model-validation.directive";
 import { ImmediateErrorStateMatcher } from "./formulaire/immediate-error-state-matcher";
+import { ParamComputedComponent } from "./components/param-computed/param-computed.component";
 
 const appRoutes: Routes = [
   { path: "list", component: CalculatorListComponent },
@@ -94,6 +100,7 @@ const appRoutes: Routes = [
     HttpClientModule,
     FlexLayoutModule,
     MatButtonModule,
+    MatButtonToggleModule,
     MatCardModule,
     MatCheckboxModule,
     MatDialogModule,
@@ -107,7 +114,6 @@ const appRoutes: Routes = [
     MatSidenavModule,
     MatTabsModule,
     MatToolbarModule,
-    MDBBootstrapModule.forRoot(),
     NgxMdModule.forRoot(),
     RouterModule.forRoot(
       appRoutes,
@@ -128,12 +134,17 @@ const appRoutes: Routes = [
     CheckFieldLineComponent,
     DialogConfirmCloseCalcComponent,
     DialogConfirmEmptySessionComponent,
+    DialogEditParamComputedComponent,
+    DialogEditParamValuesComponent,
     DialogLoadSessionComponent,
     DialogSaveSessionComponent,
     FieldSetComponent,
     FieldsetContainerComponent,
     FixedResultsComponent,
     FixedVarResultsComponent,
+    FlexGtXxsShowHideDirective,
+    FlexLtXsShowHideDirective,
+    FlexXxsShowHideDirective,
     GenericCalculatorComponent,
     GraphTypeSelectComponent,
     HorizontalResultElementComponent,
@@ -142,9 +153,7 @@ const appRoutes: Routes = [
     LogComponent,
     LogEntryComponent,
     NgParamInputComponent,
-    NgParamMaxComponent,
-    NgParamMinComponent,
-    NgParamStepComponent,
+    ParamComputedComponent,
     ParamFieldLineComponent,
     ParamLinkComponent,
     ParamValuesComponent,
@@ -154,18 +163,20 @@ const appRoutes: Routes = [
     SectionCanvasComponent,
     SectionResultsComponent,
     SelectFieldLineComponent,
-    ValueListComponent,
     VarResultsComponent,
     VerticalResultElementComponent
   ],
   entryComponents: [
     DialogConfirmCloseCalcComponent,
     DialogConfirmEmptySessionComponent,
+    DialogEditParamComputedComponent,
+    DialogEditParamValuesComponent,
     DialogSaveSessionComponent,
     DialogLoadSessionComponent
   ],
   providers: [ // services
     ApplicationSetupService,
+    CustomBreakPointsProvider,
     FormulaireService,
     HttpService,
     I18nService,
diff --git a/src/app/components/base-param-input/base-param-input.component.ts b/src/app/components/base-param-input/base-param-input.component.ts
index a919e31a7c75ab8750a93f8a96b9c0dfc19c5d72..fb65a845cc5c4c2d59a99968641c67d9e9fb5486 100644
--- a/src/app/components/base-param-input/base-param-input.component.ts
+++ b/src/app/components/base-param-input/base-param-input.component.ts
@@ -112,25 +112,4 @@ export class BaseParamInputComponent extends GenericInputComponent {
     protected validateModelValue(v: any): { isValid: boolean, message: string } {
         return this._model.validateModelValue(v);
     }
-
-    protected modelToUI(v: any): string {
-        return String(v);
-    }
-
-    protected validateUIValue(ui: string): { isValid: boolean, message: string } {
-        let valid = false;
-        let msg: string;
-
-        if (! isNumeric(ui)) {
-            msg = "Veuillez entrer une valeur numérique";
-        } else {
-            valid = true;
-        }
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected uiToModel(ui: string) {
-        return +ui;
-    }
 }
diff --git a/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.html b/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..9efaf288795c96ec273dc07655f1f13889cfad2a
--- /dev/null
+++ b/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.html
@@ -0,0 +1,16 @@
+<h1 mat-dialog-title [innerHTML]="uitextEditParamComputedInitialValue"></h1>
+
+<form>
+
+  <div mat-dialog-content>
+    <ngparam-input [title]="param.title" ></ngparam-input>
+    <!-- (change)="onInputChange($event)" -->
+  </div>
+
+  <div mat-dialog-actions>
+    <button mat-raised-button [mat-dialog-close]="true" cdkFocusInitial>
+      {{ uitextClose }}
+    </button>
+  </div>
+
+</form>
diff --git a/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.scss b/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..bc76a27e25280cc38af4ab2f9eb032683c35e758
--- /dev/null
+++ b/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.scss
@@ -0,0 +1,3 @@
+mat-form-field {
+    width: 100%;
+}
diff --git a/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.ts b/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e7979965fce7f80717be0d9086fddc818922c6e1
--- /dev/null
+++ b/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.ts
@@ -0,0 +1,40 @@
+import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material";
+import { Inject, Component, ViewChild, OnInit } from "@angular/core";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
+import { NgParameter } from "../../formulaire/ngparam";
+import { NgParamInputComponent } from "../ngparam-input/ngparam-input.component";
+
+@Component({
+    selector: "dialog-edit-param-computed",
+    templateUrl: "dialog-edit-param-computed.component.html",
+    styleUrls: ["dialog-edit-param-computed.component.scss"]
+})
+export class DialogEditParamComputedComponent implements OnInit {
+
+    /** the related parameter to change the "fixed" value of */
+    public param: NgParameter;
+
+    @ViewChild(NgParamInputComponent)
+    private _ngParamInputComponent: NgParamInputComponent;
+
+    constructor(
+      public dialogRef: MatDialogRef<DialogEditParamComputedComponent>,
+      private intlService: I18nService,
+      @Inject(MAT_DIALOG_DATA) public data: any
+    ) {
+      this.param = data.param;
+    }
+
+    public get uitextClose() {
+      return this.intlService.localizeText("INFO_OPTION_CLOSE");
+    }
+
+    public get uitextEditParamComputedInitialValue() {
+      return "Modifier la valeur initiale (à traduire)";
+      // return this.intlService.localizeText("INFO_OPTION_ALL");
+    }
+
+    public ngOnInit() {
+      this._ngParamInputComponent.model = this.param;
+    }
+}
diff --git a/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.html b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..61ae3b59b26228ab9f65690ea52dea034bfc38ae
--- /dev/null
+++ b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.html
@@ -0,0 +1,84 @@
+<h1 mat-dialog-title [innerHTML]="uitextEditParamComputedInitialValue"></h1>
+
+  <div mat-dialog-content>
+
+    <mat-form-field>
+      <mat-select [placeholder]="uiTextModeSelection" [(value)]="selectedValueMode" (selectionChange)="onValueModeChange($event)"
+        data-testid="variable-value-mode-select">
+          <mat-option *ngFor="let e of valueModes" [value]="e.value">
+              {{ e.label }}
+          </mat-option>
+      </mat-select>
+    </mat-form-field>
+
+    <div *ngIf="isMinMax" class="min-max-step-container">
+        <form>
+            <mat-form-field>
+                <input matInput class="form-control" type="number" inputmode="numeric" name="min-value" min="0" step="0.01"
+                    [placeholder]="uitextValeurMini" [(ngModel)]="param.minValue" required>
+
+                <mat-error>{{ errorMessage }}</mat-error>
+            </mat-form-field>
+
+            <mat-form-field>
+                <input matInput class="form-control" type="number" inputmode="numeric" name="max-value" min="0" step="0.01"
+                    [placeholder]="uitextValeurMaxi" [(ngModel)]="param.maxValue" required>
+
+                <mat-error>{{ errorMessage }}</mat-error>
+            </mat-form-field>
+
+            <mat-form-field>
+                <input matInput class="form-control" type="number" inputmode="numeric" name="step-value" min="0" step="0.01"
+                    [placeholder]="uitextPasVariation" [(ngModel)]="param.stepValue" required>
+
+                <mat-error>{{ errorMessage }}</mat-error>
+            </mat-form-field>
+        </form>
+    </div>
+
+    <div *ngIf="isListe">
+        <form [formGroup]="valuesListForm">
+            <mat-form-field>
+                <textarea matInput matTextareaAutosize [placeholder]="uitextListeValeurs" [ngModel]="valuesList"
+                    name="values-list" #vl="ngModel" required [pattern]="valuesListPattern"></textarea>
+                <mat-error *ngIf="vl.errors">
+                    <div *ngIf="vl.errors.required || vl.errors.pattern">
+                        {{ uitextMustBeListOfNumbers }}
+                    </div>
+                    <!-- <div *ngIf="! vl.errors.required && vl.errors.jalhydModel">
+                        {{ vl.errors.jalhydModel.message }}
+                    </div> -->
+                </mat-error>
+            </mat-form-field>
+
+            <div class="decimal-separator-and-file-container" fxLayout="row wrap" fxLayoutAlign="space-between start">
+                <mat-form-field class="decimal-separator" fxFlex.gt-xs="1 0 auto" fxFlex.lt-sm="1 0 100%">
+                    <mat-select [placeholder]="uitextDecimalSeparator" [(value)]="decimalSeparator"
+                        data-testid="decimal-separator-select">
+                        <mat-option *ngFor="let e of decimalSeparators" [value]="e.value">
+                            {{ e.label }}
+                        </mat-option>
+                    </mat-select>
+                </mat-form-field>
+
+                <div fxHide.xs fxFlex.gt-xs="0 0 16px"></div>
+
+                <mat-form-field class="values-file" fxFlex.gt-xs="1 0 auto" fxFlex.lt-sm="1 0 100%">
+                    <ngx-mat-file-input #valuesFile [placeholder]="uitextImportFile"
+                        (change)="onFileSelected($event)" formControlName="file">
+                    </ngx-mat-file-input>
+                    <button mat-icon-button matSuffix *ngIf="!valuesFile.empty" (click)="valuesFile.clear($event)">
+                        <mat-icon>clear</mat-icon>
+                    </button>
+                </mat-form-field>
+            </div>
+        </form>
+    </div>
+
+  </div>
+
+  <div mat-dialog-actions>
+    <button mat-raised-button [mat-dialog-close]="true" cdkFocusInitial>
+      {{ uitextClose }}
+    </button>
+  </div>
diff --git a/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.scss b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..c411650ebcff70179e2e847bbff3d8a0aa08636d
--- /dev/null
+++ b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.scss
@@ -0,0 +1,16 @@
+mat-form-field {
+    display: block;
+
+    textarea {
+        font-size: .8em;
+        max-height: 100px;
+    }
+
+    /*&.decimal-separator, &.values-file {
+        display: inline-block;
+    }*/
+}
+
+.min-max-step-container {
+    margin-top: -8px;
+}
diff --git a/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.ts b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..76b32a8771b409e823883c93cb96d5b0318dff7d
--- /dev/null
+++ b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.ts
@@ -0,0 +1,271 @@
+import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material";
+import { Inject, Component, OnInit } from "@angular/core";
+import { FormBuilder, FormGroup, Validators } from "@angular/forms";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
+import { NgParameter } from "../../formulaire/ngparam";
+import { ParamValueMode } from "jalhyd";
+
+@Component({
+    selector: "dialog-edit-param-values",
+    templateUrl: "dialog-edit-param-values.component.html",
+    styleUrls: ["dialog-edit-param-values.component.scss"]
+})
+export class DialogEditParamValuesComponent implements OnInit {
+
+    /** the related parameter to change the "variable" value of */
+    public param: NgParameter;
+
+    /** available value modes (min / max, list) */
+    public valueModes;
+
+    /** available decimal separators */
+    public decimalSeparators;
+
+    /** current decimal separator */
+    public decimalSeparator;
+
+    public valuesListForm: FormGroup;
+
+    constructor(
+        public dialogRef: MatDialogRef<DialogEditParamValuesComponent>,
+        private intlService: I18nService,
+        private fb: FormBuilder,
+        @Inject(MAT_DIALOG_DATA) public data: any
+    ) {
+        this.param = data.param;
+        if (this.isMinMax) {
+            console.log("PARAM MIN VALUE", this.param.minValue);
+            console.log("PARAM MAX VALUE", this.param.maxValue);
+            console.log("PARAM STEP VALUE", this.param.stepValue);
+        }
+        if (this.isListe) {
+            console.log("PARAM LIST VALUE", this.param.valueList);
+        }
+
+        // an explicit ReactiveForm is required for file input component
+        this.valuesListForm = this.fb.group({
+            file: [null, null]
+        });
+
+        this.valueModes = [
+            {
+                value: ParamValueMode.MINMAX,
+                label: "Min/max"
+            },
+            {
+                value: ParamValueMode.LISTE,
+                label: "Liste (à traduire)"
+            }
+        ];
+        this.decimalSeparators = [
+            {
+                label: "Point (à traduire)",
+                value: "."
+            },
+            {
+                label: "Virgule (à traduire)",
+                value: ","
+            }
+        ];
+        this.decimalSeparator = this.decimalSeparators[0].value;
+    }
+
+    /** regular expression pattern for values list validation (depends on decimal separator) */
+    public get valuesListPattern() {
+        const escapedDecimalSeparator = (this.decimalSeparator === "." ? "\\." : this.decimalSeparator);
+        const numberSubPattern = "-?([0-9]+" + escapedDecimalSeparator + ")?[0-9E]+";
+        const re = numberSubPattern + "(" + this.separatorPattern + numberSubPattern + ")*";
+        // console.log("Re PAT", re);
+        return re;
+    }
+
+    /** accepted separator: everything but [numbers, E, +, -, decimal separator], any length */
+    public get separatorPattern() {
+        return "[^0-9-+E" + this.decimalSeparator  + "]+";
+    }
+
+    public get selectedValueMode() {
+        return this.param.valueMode;
+    }
+
+    public set selectedValueMode(v) {
+        this.param.valueMode = v;
+    }
+
+    public get uiTextModeSelection() {
+        return "Mode de variabilité (à traduire)";
+    }
+
+    public get uitextValeurMini() {
+        return this.intlService.localizeText("INFO_PARAMFIELD_VALEURMINI");
+    }
+
+    public get uitextValeurMaxi() {
+        return this.intlService.localizeText("INFO_PARAMFIELD_VALEURMAXI");
+    }
+
+    public get uitextPasVariation() {
+        return this.intlService.localizeText("INFO_PARAMFIELD_PASVARIATION");
+    }
+
+    public get uitextClose() {
+        return this.intlService.localizeText("INFO_OPTION_CLOSE");
+    }
+
+    public get uitextEditParamComputedInitialValue() {
+        return "Modifier les valeurs variables (à traduire)";
+        // return this.intlService.localizeText("INFO_OPTION_ALL");
+    }
+
+    public get uitextListeValeurs() {
+        return "valeurs séparées par ';' (à traduire)";
+    }
+
+    public get uitextMustBeListOfNumbers() {
+        return "les valeurs doivent être de la forme 3;4.5;6 (à traduire)";
+    }
+
+    public get uitextDecimalSeparator() {
+        return "Séparateur décimal (à traduire)";
+    }
+
+    public get uitextImportFile() {
+        return "Importer un fichier (à traduire)";
+    }
+
+    public get isMinMax() {
+        return this.param.valueMode === ParamValueMode.MINMAX;
+    }
+
+    public get isListe() {
+        return this.param.valueMode === ParamValueMode.LISTE;
+    }
+
+    public get valuesList() {
+        return (this.param.valueList || []).join(";");
+    }
+
+    public set valuesList(list: string) {
+        const vals = [];
+        list.split(";").forEach((e) => {
+            if (e.length > 0) {
+                vals.push(Number(e));
+            }
+        });
+        this.param.valueList = vals;
+    }
+
+    public onFileSelected(event: any) {
+        if (event.target.files && event.target.files.length) {
+            console.log("FICHIERS", event.target.files);
+            const fr = new FileReader();
+            fr.onload = () => {
+                console.log("CHARJAY", fr.result);
+                this.valuesList = String(fr.result);
+            };
+            fr.onerror = () => {
+                fr.abort();
+                throw new Error("Erreur de lecture du fichier");
+            };
+            fr.readAsText(event.target.files[0]);
+        }
+    }
+
+    public dumperr(obj) {
+        if (obj) { return Object.keys(obj).join(", "); }
+    }
+
+    public onValueModeChange(event) {
+        this.initVariableValues();
+    }
+
+    public ngOnInit() {
+        this.initVariableValues();
+    }
+
+    private initVariableValues() {
+        // init min / max / step
+        if (this.isMinMax) {
+            if (this.param.minValue === undefined) {
+                this.param.minValue = this.param.getValue() / 2;
+            }
+            if (this.param.maxValue === undefined) {
+                this.param.maxValue = this.param.getValue() * 2;
+            }
+            let step = this.param.stepValue;
+            if (step === undefined) {
+                step = (this.param.maxValue - this.param.minValue) / 20;
+            }
+            this.param.stepValue = step;
+        }
+        // init values list
+        if (this.isListe) {
+            if (this.param.valueList === undefined) {
+                if (this.param.isDefined) {
+                    console.log("SET LIST", [ this.param.getValue() ]);
+                    this.param.valueList = [ this.param.getValue() ];
+                } else {
+                    console.log("SET LIST", [ ]);
+                    this.param.valueList = [];
+                }
+            }
+        }
+    }
+
+
+  /*   protected validateModelValue(v: any): { isValid: boolean, message: string } {
+      let msg: string;
+      let valid = false;
+
+      if (this.param === undefined) {
+          msg = "internal error, model undefined";
+      } else {
+          if (!this.param.checkMin(v)) {
+              msg = "La valeur n'est pas dans [" + this.param.domain.minValue + " , " + this.param.maxValue + "[";
+          } else {
+              valid = true;
+          }
+      }
+
+      return { isValid: valid, message: msg };
+  }
+  protected validateModelValue(v: any): { isValid: boolean, message: string } {
+      let msg: string;
+      let valid = false;
+
+      if (this.param === undefined) {
+          msg = "internal error, model undefined";
+      } else {
+          if (!this.param.checkMax(v)) {
+              msg = "La valeur n'est pas dans ]" + this.param.minValue + " , " + this.param.domain.maxValue + "]";
+          } else {
+              valid = true;
+          }
+      }
+
+      return { isValid: valid, message: msg };
+  }
+  protected validateModelValue(v: any): { isValid: boolean, message: string } {
+      let msg: string;
+      let valid = false;
+
+      if (! this.param) {
+          msg = "internal error, model undefined";
+      } else {
+          if (this.param.isMinMaxValid) {
+              if (!this.param.checkStep(v)) {
+                  msg = "La valeur n'est pas dans " + this.param.stepRefValue.toString();
+              } else {
+                  valid = v > 0;
+                  if (!valid) {
+                      msg = "La valeur ne peut pas être <= 0";
+                  }
+              }
+          } else {
+              msg = "Veuillez corriger le min/max";
+          }
+      }
+
+      return { isValid: valid, message: msg };
+  } */
+}
diff --git a/src/app/components/field-set/field-set.component.html b/src/app/components/field-set/field-set.component.html
index b0c0cec84f512d923cf2ad31ccea995b97a1f4cc..3217d50f0ba485c834824abf92139f58b47292d8 100644
--- a/src/app/components/field-set/field-set.component.html
+++ b/src/app/components/field-set/field-set.component.html
@@ -1,31 +1,31 @@
-<div class="row fieldset_backgrd">
-    <div class="col fieldset_title">
+<mat-card-header class="bg-accent-light">
+    <mat-card-title>
         {{ title }}
+    </mat-card-title>
+    <div *ngIf="showButtons" class="hyd-window-btns">
+        <button mat-icon-button (click)="onAddClick()">
+            <mat-icon>add_box</mat-icon>
+        </button>
+        <button mat-icon-button [disabled]="! enableRemoveButton" (click)="onRemoveClick()">
+            <mat-icon>delete</mat-icon>
+        </button>
+        <button mat-icon-button [disabled]="! enableUpButton" (click)="onMoveUpClick()">
+            <mat-icon>arrow_upward</mat-icon>
+        </button>
+        <button mat-icon-button [disabled]="! enableDownButton" (click)="onMoveDownClick()">
+            <mat-icon>arrow_downward</mat-icon>
+        </button>
     </div>
-    <div *ngIf="showButtons" class="col-sm-4 hyd-window-btns">
-        <mat-icon (click)='onAddClick()'>add</mat-icon>
-        <mat-icon [style.color]='removeButtonColor' (click)='onRemoveClick()'>delete</mat-icon>
-        <mat-icon [style.color]='upButtonColor' (click)='onMoveUpClick()'>arrow_upward</mat-icon>
-        <mat-icon [style.color]='downButtonColor' (click)='onMoveDownClick()'>arrow_downward</mat-icon>
-    </div>
-</div>
-
-<!--
-    <tag *ngFor="let var of array" *ngIf="...utilisation de var..." >
-    </tag>
-    peut être transformé en
-    <ng-template ngFor let-var [ngForOf]="array">
-        <tag *ngIf="...utilisation de var..." >
-        </tag>
-    </ng-template>
--->
+</mat-card-header>
 
-<ng-template ngFor let-p [ngForOf]="fields">
-    <param-field-line *ngIf="isInputField(p)" [param]=p (radio)=onRadioClick($event) (valid)=onParamLineValid() (inputChange)=onInputChange()>
-    </param-field-line>
+<mat-card-content>
+    <ng-template ngFor let-p [ngForOf]="fields">
+        <param-field-line *ngIf="isInputField(p)" [param]=p (radio)=onRadioClick($event) (valid)=onParamLineValid() (inputChange)=onInputChange()>
+        </param-field-line>
 
-    <select-field-line *ngIf="isSelectField(p)" [_select]=p>
-    </select-field-line>
+        <select-field-line *ngIf="isSelectField(p)" [_select]=p>
+        </select-field-line>
 
-    <check-field-line *ngIf="isCheckField(p)" [check]=p></check-field-line>
-</ng-template>
\ No newline at end of file
+        <check-field-line *ngIf="isCheckField(p)" [check]=p></check-field-line>
+    </ng-template>
+</mat-card-content>
diff --git a/src/app/components/field-set/field-set.component.scss b/src/app/components/field-set/field-set.component.scss
index a4533b19585e87ad62f56339a3d820d18d863ed8..373f5b26832ff9012eb9819ced8f1b4b23fcb914 100644
--- a/src/app/components/field-set/field-set.component.scss
+++ b/src/app/components/field-set/field-set.component.scss
@@ -1,19 +1,84 @@
-.fieldset_title {
-    font-weight: bold;
+param-field-line {
+    // border: solid green 2px;
+    display: block;
 }
-.radio_param_header {
-    width: 10em;
+
+select-field-line {
+    // border: solid blue 2px;
+    display: block;
+}
+
+mat-card-header {
+    margin-left: -16px;
+    margin-right: -16px;
+    padding-left: 16px;
+    padding-top: 8px;
+    color: white;
+
+    // Pourquoi n'est-ce pas hérité de calculator.component.scss ?
+    // À cause de la surcharge de mat-card-header ci-dessus ?
+    mat-card-title {
+        font-size: 16px;
+        margin-bottom: 8px;
+    }
+
+    .hyd-window-btns {
+        text-align: right;
+    
+        mat-icon {
+            cursor: pointer;
+            transform: scale(1.2);
+            margin-top: 6px;
+            margin-right: 5px;
+        }
+    }
 }
-.fieldset_backgrd {
-    background-color: #eeeeee;
+
+mat-card-content {
+    margin-top: 1em;
 }
-.hyd-window-btns {
-    text-align: right;
-
-    mat-icon {
-        cursor: pointer;
-        transform: scale(1.2);
-        margin-top: 6px;
-        margin-right: 5px;
+
+mat-card-actions {
+
+    button {
+        width: 100%;
+    }
+}
+
+:host {
+
+    &.fieldset-inner {
+
+        mat-card-header {
+            margin-left: 0;
+            margin-right: 0;
+            padding-left: 12px;
+
+            mat-card-title {
+                font-size: 14px;
+            }
+            
+            .hyd-window-btns {
+                position: absolute;
+                right: 18px;
+                margin-top: -4px;
+
+                button.mat-icon-button {
+                    width: 26px;
+                    height: 20px;
+                    line-height: 20px;
+
+                    mat-icon {
+                        margin-top: 0;
+                        margin-right: 0;
+                        transform: scale(1);
+                    }
+                }
+            }
+        }
+
+        mat-card-content {
+            padding: 0 1em;
+        }
     }
 }
diff --git a/src/app/components/field-set/field-set.component.ts b/src/app/components/field-set/field-set.component.ts
index 24a031d4df575d78117d2adf4c3c1e24bb8475a2..5848aa4c1f961ab145c586232f86d6717386db71 100644
--- a/src/app/components/field-set/field-set.component.ts
+++ b/src/app/components/field-set/field-set.component.ts
@@ -24,26 +24,6 @@ export class FieldSetComponent implements DoCheck {
         return this._fieldSet.kids;
     }
 
-    public get showButtons() {
-        return this._showButtons;
-    }
-
-    public set showButtons(b: boolean) {
-        this._showButtons = b;
-    }
-
-    public set enableUpButton(b: boolean) {
-        this._enableUpButton = b;
-    }
-
-    public set enableDownButton(b: boolean) {
-        this._enableDownButton = b;
-    }
-
-    public set enableRemoveButton(b: boolean) {
-        this._enableRemoveButton = b;
-    }
-
     public set fieldsetNumber(n: number) {
         this._fieldSet.labelNumber = n;
     }
@@ -62,26 +42,6 @@ export class FieldSetComponent implements DoCheck {
         return this._isValid;
     }
 
-    /**
-     * couleur du bouton monter
-     */
-    private get upButtonColor(): string {
-        return this._enableUpButton ? "black" : "lightgrey";
-    }
-
-    /**
-     * couleur du bouton descendre
-     */
-    private get downButtonColor(): string {
-        return this._enableDownButton ? "black" : "lightgrey";
-    }
-
-    /**
-     * couleur du bouton supprimer
-     */
-    private get removeButtonColor(): string {
-        return this._enableRemoveButton ? "black" : "lightgrey";
-    }
     /**
     * field set attribute
     */
@@ -134,22 +94,22 @@ export class FieldSetComponent implements DoCheck {
     /**
      * flag d'affichage des boutons ajouter, supprimer, monter, descendre
      */
-    private _showButtons = false;
+    public showButtons = false;
 
     /**
      * flag d'activation du bouton monter
      */
-    private _enableUpButton = true;
+    public enableUpButton = true;
 
     /**
      * flag d'activation du bouton descendre
      */
-    private _enableDownButton = true;
+    public enableDownButton = true;
 
     /**
      * flag d'activation du bouton supprimer
      */
-    private _enableRemoveButton = true;
+    public enableRemoveButton = true;
 
     /**
      * événement de changement d'état d'un radio
@@ -300,26 +260,20 @@ export class FieldSetComponent implements DoCheck {
      * clic sur le bouton supprimer
      */
     private onRemoveClick() {
-        if (this._enableRemoveButton) {
-            this.removeFieldset.emit(this._fieldSet);
-        }
+        this.removeFieldset.emit(this._fieldSet);
     }
 
     /**
      * clic sur le bouton monter
      */
     private onMoveUpClick() {
-        if (this._enableUpButton) {
-            this.moveFieldsetUp.emit(this._fieldSet);
-        }
+        this.moveFieldsetUp.emit(this._fieldSet);
     }
 
     /**
      * clic sur le bouton descendre
      */
     private onMoveDownClick() {
-        if (this._enableDownButton) {
-            this.moveFieldsetDown.emit(this._fieldSet);
-        }
+        this.moveFieldsetDown.emit(this._fieldSet);
     }
 }
diff --git a/src/app/components/fieldset-container/fieldset-container.component.html b/src/app/components/fieldset-container/fieldset-container.component.html
index 84e4f78e5a5c47b81372383aafb6a8126856e52f..08e323f4465f7fad1027429de458464fbd5232c8 100644
--- a/src/app/components/fieldset-container/fieldset-container.component.html
+++ b/src/app/components/fieldset-container/fieldset-container.component.html
@@ -1,10 +1,13 @@
-<div class="container-fluid" style="border-style:solid; border-color: lightgray; border-radius: 10px; margin-bottom: 10px;">
-    <div class="row">
-        <h4 class="col">{{ title }}</h4>
-    </div>
+<mat-card-header class="bg-accent-light">
+    <mat-card-title>
+        {{ title }}
+    </mat-card-title>
+</mat-card-header>
 
-    <field-set *ngFor="let fs of fieldsets" [fieldSet]=fs (radio)=onRadioClick($event) (valid)=onFieldsetValid() (inputChange)=onInputChange()
-        (addFieldset)=onAddFieldset($event) (removeFieldset)=onRemoveFieldset($event) (moveFieldsetUp)=onMoveFieldsetUp($event)
-        (moveFieldsetDown)=onMoveFieldsetDown($event)>
+<mat-card-content>
+    <field-set *ngFor="let fs of fieldsets" class="fieldset-inner" [fieldSet]=fs
+        (radio)=onRadioClick($event) (valid)=onFieldsetValid() (inputChange)=onInputChange()
+        (addFieldset)=onAddFieldset($event) (removeFieldset)=onRemoveFieldset($event)
+        (moveFieldsetDown)=onMoveFieldsetDown($event) (moveFieldsetUp)=onMoveFieldsetUp($event)>
     </field-set>
-</div>
\ No newline at end of file
+</mat-card-content>
diff --git a/src/app/components/fieldset-container/fieldset-container.component.scss b/src/app/components/fieldset-container/fieldset-container.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..bbc65715fe3bf96979687811fbac7ffc9273c3d0
--- /dev/null
+++ b/src/app/components/fieldset-container/fieldset-container.component.scss
@@ -0,0 +1,26 @@
+:host {
+    display: block;
+    background-color: #f0f0f0;
+    // reduce margins to avoid inner field-sets being too narrow on 360px display
+    margin-left: -8px;
+    margin-right: -8px;
+}
+
+mat-card-header {
+    margin-left: -8px;
+    margin-right: -8px;
+    padding-left: 16px;
+    padding-top: 8px;
+    color: white;
+
+    // Pourquoi n'est-ce pas hérité de calculator.component.scss ?
+    // À cause de la surcharge de mat-card-header ci-dessus ?
+    mat-card-title {
+        font-size: 16px;
+        margin-bottom: 8px;
+    }
+}
+
+mat-card-content {
+    margin-top: 1em;
+}
diff --git a/src/app/components/fieldset-container/fieldset-container.component.ts b/src/app/components/fieldset-container/fieldset-container.component.ts
index 36c8199c3098e601edbafad47f6f2bb9706e8cf7..75caa880fcfb96ab8300ad8a00a09e162b39874a 100644
--- a/src/app/components/fieldset-container/fieldset-container.component.ts
+++ b/src/app/components/fieldset-container/fieldset-container.component.ts
@@ -7,7 +7,10 @@ import { FormulaireDefinition } from "../../formulaire/definition/form-definitio
 
 @Component({
     selector: "fieldset-container",
-    templateUrl: "./fieldset-container.component.html"
+    templateUrl: "./fieldset-container.component.html",
+    styleUrls: [
+        "./fieldset-container.component.scss"
+    ]
 })
 export class FieldsetContainerComponent implements DoCheck, AfterViewInit {
 
diff --git a/src/app/components/generic-calculator/calc-name.component.scss b/src/app/components/generic-calculator/calc-name.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..8e8060ff4581e463b045a4012cef43024867b57c
--- /dev/null
+++ b/src/app/components/generic-calculator/calc-name.component.scss
@@ -0,0 +1,11 @@
+:host {
+    display: block;
+}
+
+mat-form-field {
+    width: 100%;
+
+    &.mat-form-field-invalid {
+        margin-bottom: 1em;
+    }
+}
diff --git a/src/app/components/generic-calculator/calc-name.component.ts b/src/app/components/generic-calculator/calc-name.component.ts
index 860ed423f6b21c8e557598303328ab4748ff4aef..a682903bd03a6babcb2e6a0c66f14a3169b97607 100644
--- a/src/app/components/generic-calculator/calc-name.component.ts
+++ b/src/app/components/generic-calculator/calc-name.component.ts
@@ -5,6 +5,9 @@ import { FormulaireDefinition } from "../../formulaire/definition/form-definitio
 @Component({
     selector: "calc-name",
     templateUrl: "../generic-input/generic-input.component.html",
+    styleUrls: [
+        "./calc-name.component.scss"
+    ]
 })
 export class CalculatorNameComponent extends GenericInputComponent {
 
@@ -55,13 +58,6 @@ export class CalculatorNameComponent extends GenericInputComponent {
         return { isValid: valid, message: msg };
     }
 
-    /**
-     * convertit le modèle en valeur affichable par l'UI
-     */
-    protected modelToUI(v: any): string {
-        return v;
-    }
-
     /**
      * valide une valeur saisie dans l'UI (forme de la saisie : est ce bien une date, un nombre, ...)
      * @param ui valide la valeur saisie
diff --git a/src/app/components/generic-calculator/calculator.component.html b/src/app/components/generic-calculator/calculator.component.html
index 2d00e9c06ad6ee68733e7dcd2808721a5895965e..e2820e129aaa54123c0c44a86e936d815dabd08e 100644
--- a/src/app/components/generic-calculator/calculator.component.html
+++ b/src/app/components/generic-calculator/calculator.component.html
@@ -1,53 +1,64 @@
-<div class="row">
-    <!-- titre -->
-    <div class="col">
-        <!-- on utilise [innerHTML] pour que les codes HTML comme &nbsp; soient interprétés correctement -->
-        <h1 [innerHTML]="uitextTitre"></h1>
-    </div>
-
-    <div class="col-sm-3 px-0 mx-0 hyd-window-btns">
-        <!-- bouton d'aide -->
-        <mat-icon *ngIf="enableHelpButton" (click)="openHelp()" color="accent">help</mat-icon>
-        <!-- bouton de sauvegarde -->
-        <mat-icon (click)="saveCalculator()">save_alt</mat-icon>
-        <!-- bouton de fermeture -->
-        <mat-icon (click)="closeCalculator()">close</mat-icon>
-    </div>
-</div>
-
-<!-- nom de la calculette -->
-<div class="row">
-    <div class="col-md-6">
-        <calc-name title="Nom de la calculette"></calc-name>
-    </div>
-</div>
-
-<div class="row">
-    <div [ngClass]="(hasResults) ? 'col-12 col-lg-6' : 'col-12'">
-        <div class="container-fluid">
-            <!-- chapitres -->
-            <ng-template ngFor let-fe [ngForOf]="formElements">
-                <field-set *ngIf="isFieldset(fe)" [style.display]="getFieldsetStyleDisplay(fe.id)" [fieldSet]=fe (radio)=onRadioClick($event)
-                    (validChange)=OnFieldsetValid() (inputChange)=onInputChange()></field-set>
-
-                <fieldset-container *ngIf="isFieldsetContainer(fe)" [_container]=fe (radio)=onRadioClick($event) (validChange)=onFieldsetContainerValid()></fieldset-container>
-            </ng-template>
+<mat-card id="calculator-card">
+
+    <mat-card-header>
+
+        <div class="hyd-window-btns">
+            <!-- bouton d'aide -->
+            <mat-icon *ngIf="enableHelpButton" (click)="openHelp()" color="accent">help</mat-icon>
+            <!-- bouton de sauvegarde -->
+            <mat-icon (click)="saveCalculator()">save_alt</mat-icon>
+            <!-- bouton de fermeture -->
+            <mat-icon (click)="closeCalculator()">close</mat-icon>
         </div>
 
-        <!-- bouton calculer -->
-        <div class="row">
-            <div class="col-12 text-center">
-                <button type="button" [ngClass]="(isCalculateDisabled) ? 'button_compute_err' : 'button_compute_ok'" name="Calculer" (click)="doCompute()"
-                    [disabled]="isCalculateDisabled">{{ uitextCalculer }}</button>
-                <p></p>
-                <p></p>
+        <!-- titre -->
+        <!-- on utilise [innerHTML] pour que les codes HTML comme &nbsp; soient interprétés correctement -->
+        <mat-card-title>
+            <h1 [innerHTML]="uitextTitre"></h1>
+        </mat-card-title>
+
+    </mat-card-header>
+
+    <form>
+
+        <mat-card-content>
+
+            <!-- nom de la calculette -->
+            <calc-name [title]="uitextCalculatorName"></calc-name>
+
+            <div id="calc-cards-container" class="container" fxLayout="row wrap" fxLayoutAlign="space-around start">
+                <!-- chapitres -->
+                <mat-card id="calc-card-field-sets" fxFlex.gt-xs="1 0 400px" fxFlex.lt-sm="1 0 300px">
+                    <ng-template ngFor let-fe [ngForOf]="formElements">
+                        <field-set *ngIf="isFieldset(fe)" [style.display]="getFieldsetStyleDisplay(fe.id)" [fieldSet]=fe (radio)=onRadioClick($event)
+                            (validChange)=OnFieldsetValid() (inputChange)=onInputChange()></field-set>
+
+                        <fieldset-container *ngIf="isFieldsetContainer(fe)" [_container]=fe (radio)=onRadioClick($event) (validChange)=onFieldsetContainerValid()></fieldset-container>
+                    </ng-template>
+
+                    <mat-card-actions>
+                        <!-- bouton calculer -->
+                        <button mat-raised-button color="accent" name="Calculer" (click)="doCompute()"[disabled]="isCalculateDisabled">
+                            {{ uitextCalculer }}
+                        </button>
+                    </mat-card-actions>
+                </mat-card>
+
+                <!-- résultats -->
+                <mat-card id="calc-card-results" fxFlex.gt-xs="1 0 400px" fxFlex.lt-sm="1 0 300px">
+                    <mat-card-header>
+                        <mat-card-title>
+                            Resultats (à traduire)
+                        </mat-card-title>
+                    </mat-card-header>
+                    <mat-card-content>
+                        <calc-results id="resultsComp" (afterViewChecked)="onCalcResultsViewChecked()"></calc-results>
+                    </mat-card-content>
+                </mat-card>
+
             </div>
-        </div>
+        </mat-card-content>
 
-    </div>
+    </form>
 
-    <!-- résultats -->
-    <div [ngClass]="(hasResults) ? 'col-12 col-lg-6' : 'col-12'">
-        <calc-results id="resultsComp" (afterViewChecked)="onCalcResultsViewChecked()"></calc-results>
-    </div>
-</div>
+</mat-card>
diff --git a/src/app/components/generic-calculator/calculator.component.scss b/src/app/components/generic-calculator/calculator.component.scss
index 10f09de7a339c20643881ac17fbb7bfa7e334e5f..699991e24ab4ce9867da4432d7ded52d9c05d46d 100644
--- a/src/app/components/generic-calculator/calculator.component.scss
+++ b/src/app/components/generic-calculator/calculator.component.scss
@@ -1,20 +1,60 @@
+.hyd-window-btns {
+    position: absolute;
+    right: 1em;
+    text-align: right;
 
-    .button_compute_ok {
-        height: 3em;
-        width: 30%;
+    mat-icon {
+        cursor: pointer;
+        margin-right: 5px;
     }
+}
 
-    .button_compute_err {
-        height: 3em;
-        width: 30%;
-        color: red
+#calc-cards-container {
+    margin-left: -1em;
+    margin-right: -1em;
+}
+
+mat-card {
+    margin-bottom: 2em;
+
+    // main card
+    &#calculator-card {
+
+        > mat-card-header {
+            margin-bottom: .5em;
+            padding-right: 6em; // space for right corner buttons
+        }
+    }
+
+    // cards inside main card
+
+    &#calc-card-field-sets {
+        margin-left: 1em;
+        margin-right: 1em;
+
+        mat-card-actions {
+            button {
+                width: 100%;
+            }
+        }
     }
 
-    .hyd-window-btns {
-        text-align: right;
+    &#calc-card-results {
+        margin-left: 1em;
+        margin-right: 1em;
+    }
 
-        mat-icon {
-            cursor: pointer;
-            margin-right: 5px;
+    // @WARNING ::ng-deep est déprécié, mais y a rien d'autre pour
+    // l'instant (en attente de normalisation par le W3C)
+    ::ng-deep .mat-card-header-text {
+        margin: 0;
+    }
+
+    mat-card-header {
+
+        mat-card-title {
+            font-size: 16px;
+            margin-bottom: 8px;
         }
-    }
\ No newline at end of file
+    }
+}
diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts
index bdde67531b94434b28f8b54c93b6d4cbeefd1379..e696082cb0e3409287247f1b1810a1b5f17100ef 100644
--- a/src/app/components/generic-calculator/calculator.component.ts
+++ b/src/app/components/generic-calculator/calculator.component.ts
@@ -138,6 +138,10 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
         return this.intlService.localizeText("INFO_CALCULATOR_CALCULER");
     }
 
+    public get uitextCalculatorName() {
+        return this.intlService.localizeText("INFO_CALCULATOR_CALC_NAME");
+    }
+
     ngOnInit() {
         this.intlService.addObserver(this);
         this.formulaireService.addObserver(this);
diff --git a/src/app/components/generic-input/generic-input.component.html b/src/app/components/generic-input/generic-input.component.html
index 1f81ebe23535ecddd6fc1f3d1d846c80ec8be0c8..28d062b05ba539d9f0419b166ddae09068d9aaa3 100644
--- a/src/app/components/generic-input/generic-input.component.html
+++ b/src/app/components/generic-input/generic-input.component.html
@@ -1,6 +1,7 @@
-<div class="md-form form-sm">
-    <input mdbInputDirective [mdbValidate]="false" type="text" [id]="inputId" class="form-control" [disabled]="isDisabled" [(ngModel)]="uiValue">
-    <!-- on utilise [innerHTML] pour que les codes HTML comme &nbsp; soient interprétés correctement -->
-    <label [for]="inputId" [innerHTML]="title"></label>
-    <small *ngIf="showError" class="text-danger" [innerHTML]="errorMessage"></small>
-</div>
\ No newline at end of file
+<mat-form-field>
+    <input matInput class="form-control" type="text" inputmode="numeric"
+        [id]="inputId" [disabled]="isDisabled" [(ngModel)]="uiValue" [placeholder]="title"
+        required>
+
+    <mat-error>{{ errorMessage }}</mat-error>
+</mat-form-field>
diff --git a/src/app/components/generic-input/generic-input.component.ts b/src/app/components/generic-input/generic-input.component.ts
index cd3e9e7dcbfe8478df253eeb4cc1a0b6037caf79..15c56f546c23552b27be30af22e17ab9211b5d8b 100644
--- a/src/app/components/generic-input/generic-input.component.ts
+++ b/src/app/components/generic-input/generic-input.component.ts
@@ -1,17 +1,7 @@
 import { Input, Output, EventEmitter, ChangeDetectorRef, OnChanges } from "@angular/core";
 
 import { BaseComponent } from "../base/base.component";
-
-/*
-exemple de template :
-
-<div class="md-form form-sm">
-    <input mdbActive type="text" id="form1" class="form-control" [disabled]="isDisabled"
-        [ngModel]="uiValue" (ngModelChange)="setUIValue($event)">
-    <label for="form1">{{title}}</label>
-    <small class="text-danger">{{_message}}</small>
-</div>
-*/
+import { isNumeric } from "jalhyd";
 
 /**
  * classe de gestion générique d'un champ de saisie avec titre, validation et message d'erreur
@@ -160,7 +150,7 @@ export abstract class GenericInputComponent extends BaseComponent implements OnC
      * getter du message d'erreur affiché.
      * L'erreur de forme (UI) est prioritaire
      */
-    private get errorMessage() {
+    public get errorMessage() {
         if (this._errorMessageUI !== undefined) {
             return this._errorMessageUI;
         }
@@ -268,7 +258,9 @@ export abstract class GenericInputComponent extends BaseComponent implements OnC
     /**
      * convertit le modèle en valeur affichable par l'UI
      */
-    protected abstract modelToUI(v: any): string;
+    protected modelToUI(v: any): string {
+        return String(v);
+    }
 
     /**
      * valide une valeur saisie dans l'UI (forme de la saisie : est ce bien une date, un nombre, ...)
@@ -276,12 +268,25 @@ export abstract class GenericInputComponent extends BaseComponent implements OnC
      * @returns isValid : true si la valeur est valide, false sinon
      * @returns message : message d'erreur
      */
-    protected abstract validateUIValue(ui: string): { isValid: boolean, message: string };
+    protected validateUIValue(ui: string): { isValid: boolean, message: string } {
+        let valid = false;
+        let msg: string;
+
+        if (! isNumeric(ui)) {
+            msg = "Veuillez entrer une valeur numérique";
+        } else {
+            valid = true;
+        }
+
+        return { isValid: valid, message: msg };
+    }
 
     /**
      * convertit une valeur saisie dans l'UI en valeur affectable au modèle
      */
-    protected abstract uiToModel(ui: string): any;
+    protected uiToModel(ui: string): any {
+        return +ui;
+    }
 }
 
 /*
@@ -298,7 +303,7 @@ import { isNumeric, Message } from "jalhyd";
 @Component({
     selector: "test-input",
     template: `<div class="md-form form-sm">
-    <input mdbActive type="text" id="form1" class="form-control" [disabled]="isDisabled" [(ngModel)]="uiValue">
+    <input  type="text" id="form1" class="form-control" [disabled]="isDisabled" [(ngModel)]="uiValue">
     <label for="form1">{{title}}</label>
     <small *ngIf="showError" class="text-danger">{{errorMessage}}</small>
 </div>`
@@ -329,23 +334,6 @@ export class TestInputComponent extends GenericInputComponent {
         return { isValid: valid, message: msg };
     }
 
-    protected modelToUI(v: any): string {
-        if (typeof (v) === "number")
-            return String(v);
-    }
-
-    protected validateUIValue(ui: string): { isValid: boolean, message: string } {
-        let valid: boolean = false;
-        let msg: string = undefined;
-
-        if (! isNumeric(ui))
-            msg = "Veuillez entrer une valeur numérique";
-        else
-            valid = true;
-
-        return { isValid: valid, message: msg };
-    }
-
     protected uiToModel(ui: string): any {
         return +ui;
     }
@@ -359,7 +347,7 @@ import { ParamDefinition } from "jalhyd";
 @Component({
     selector: "test2-input",
     template: `<div class="md-form form-sm">
-    <input mdbActive type="text" id="form1" class="form-control" [disabled]="isDisabled" [(ngModel)]="uiValue">
+    <input type="text" id="form1" class="form-control" [disabled]="isDisabled" [(ngModel)]="uiValue">
     <label for="form1">{{title}}</label>
     <small *ngIf="showError" class="text-danger">{{errorMessage}}</small>
 </div>`
@@ -393,26 +381,5 @@ export class Test2InputComponent extends GenericInputComponent {
 
         return { isValid: valid, message: msg };
     }
-
-    protected modelToUI(v: any): string {
-        if (typeof (v) === "number")
-            return String(v);
-    }
-
-    protected validateUIValue(ui: string): { isValid: boolean, message: string } {
-        let valid: boolean = false;
-        let msg: string = undefined;
-
-        if (! isNumeric(ui))
-            msg = "Veuillez entrer une valeur numérique";
-        else
-            valid = true;
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected uiToModel(ui: string): any {
-        return +ui;
-    }
 }
 /**/
diff --git a/src/app/components/generic-select/generic-select.component.html b/src/app/components/generic-select/generic-select.component.html
index 8edaab39d123f794448d7abf0090a5cc81ad6419..da7a82515f0406dfc56beb7a36b4e61fc37ac7ac 100644
--- a/src/app/components/generic-select/generic-select.component.html
+++ b/src/app/components/generic-select/generic-select.component.html
@@ -1,8 +1,7 @@
-<div class="btn-group" dropdown (click)="onSelect($event)">
-    <button dropdownToggle class="btn btn-primary dropdown-toggle waves-light my-1" type="button" mdbRippleRadius>
-        {{ currentLabel }}
-    </button>
-    <div class="dropdown-menu">
-        <a class="dropdown-item" *ngFor="let e of entries" [value]=e>{{ entryLabel(e) }}</a>
-    </div>
-</div>
\ No newline at end of file
+<mat-form-field>
+    <mat-select [placeholder]="label" [(value)]="selectedValue">
+        <mat-option *ngFor="let e of entries" [value]="e">
+            {{ entryLabel(e) }}
+        </mat-option>
+    </mat-select>
+</mat-form-field>
diff --git a/src/app/components/generic-select/generic-select.component.ts b/src/app/components/generic-select/generic-select.component.ts
deleted file mode 100644
index 6614882ab63ed2f13e6b8fdcfe329dc97f7a6d80..0000000000000000000000000000000000000000
--- a/src/app/components/generic-select/generic-select.component.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-import { Component, Output, EventEmitter } from "@angular/core";
-import { BaseComponent } from "../base/base.component";
-
-/*
-  exemple de template :
-
-<div class="btn-group" dropdown (click)="onSelect($event)">
-    <button dropdownToggle class="btn btn-primary dropdown-toggle waves-light my-1" type="button" mdbRippleRadius>
-        {{currentLabel}}
-    </button>
-    <div class="dropdown-menu">
-        <a class="dropdown-item" *ngFor="let e of entries" [value]=e>{{entryLabel(e)}}</a>
-    </div>
-</div>
-*/
-
-export abstract class GenericSelectComponent<T> {
-
-    private _currentLabel: string;
-
-    public get currentLabel(): string {
-        if (! this._currentLabel) {
-            this._currentLabel = this.selectedLabel;
-        }
-        return this._currentLabel;
-    }
-
-    /**
-     * appelé quand la valeur courante change dans l'UI
-     */
-    public onSelect(event: any) {
-        const val = event.target.value;
-        if (val !== undefined && val !== "") { // might be 0; opening the menu returns ""
-            this.selectedValue = val;
-            this._currentLabel = this.selectedLabel;
-        }
-    }
-
-    private get selectedLabel(): string {
-        for (const e of this.entries) {
-            if (e === this.selectedValue) {
-                return this.entryLabel(e);
-            }
-        }
-    }
-
-    /**
-     * liste des objets sélectionnables
-     */
-    public abstract get entries(): any[];
-
-    /**
-     * calcule l'étiquette d'une entrée (ce qui est affiché dans la liste déroulante)
-     */
-    protected abstract entryLabel(entry: any): string;
-
-    /**
-     *  valeur actuellement sélectionnée
-     * la valeur repère une entrée de la liste (cf. [value] dans le template)
-     */
-    public abstract get selectedValue(): T;
-
-
-    public abstract set selectedValue(v: T);
-}
diff --git a/src/app/components/log-entry/log-entry.component.html b/src/app/components/log-entry/log-entry.component.html
index 2c2194f95f08e08a3afb090b4cad04b4c5d27e47..4e725d2a88c841262f986d31f5455c7ad93137cf 100644
--- a/src/app/components/log-entry/log-entry.component.html
+++ b/src/app/components/log-entry/log-entry.component.html
@@ -1,10 +1,10 @@
-<div class="row entry">
-    <div class="col-1" style="text-align: center;">
+<div class="entry">
+    <div style="text-align: center;">
         <mat-icon *ngIf="levelInfo" style="color:green">check_circle</mat-icon>
         <mat-icon *ngIf="levelWarning" style="color:orange">error_outline</mat-icon>
         <mat-icon *ngIf="levelError" style="color:red">warning</mat-icon>
     </div>
-    <div class="col-11">
+    <div>
         <!-- on utilise [innerHTML] pour que les codes HTML comme &nbsp; soient interprétés correctement -->
         <span [innerHTML]="text"></span>
     </div>
diff --git a/src/app/components/log/log.component.html b/src/app/components/log/log.component.html
index 15d92c0f171cb2dff50912c2fd91978c5af3a986..c2c1364aa186042151630abbe2e7535346d5cfde 100644
--- a/src/app/components/log/log.component.html
+++ b/src/app/components/log/log.component.html
@@ -1,13 +1,11 @@
-<div class="row" *ngIf="hasEntries">
-    <div class="col-12">
-        <div class="hyd_log">
-            <!-- titre -->
-            <div class="titre">{{ uitextTitreJournal }}</div>
+<div *ngIf="hasEntries">
+    <div class="hyd_log">
+        <!-- titre -->
+        <div class="titre">{{ uitextTitreJournal }}</div>
 
-            <!-- entrées du journal -->
-            <ng-template ngFor let-m [ngForOf]="messages">
-                <log-entry [_message]=m></log-entry>
-            </ng-template>
-        </div>
+        <!-- entrées du journal -->
+        <ng-template ngFor let-m [ngForOf]="messages">
+            <log-entry [_message]=m></log-entry>
+        </ng-template>
     </div>
 </div>
\ No newline at end of file
diff --git a/src/app/components/ngparam-input/ngparam-input.component.scss b/src/app/components/ngparam-input/ngparam-input.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..cf6a916663aeed73da3d76250f2cc012463da1e5
--- /dev/null
+++ b/src/app/components/ngparam-input/ngparam-input.component.scss
@@ -0,0 +1,13 @@
+:host {
+    display: block;
+    margin-top: 14px;
+}
+
+mat-form-field {
+    width: calc(100% - 16px);
+    margin-right: 16px;
+    
+    ::ng-deep input.mat-input-element {
+        line-height: 1.3em;
+    }
+}
diff --git a/src/app/components/ngparam-input/ngparam-input.component.ts b/src/app/components/ngparam-input/ngparam-input.component.ts
index 11d004d7514adada257030522afdc89a7f785693..69be65ac350b3d7b23272a41d9d07653c06a1fd0 100644
--- a/src/app/components/ngparam-input/ngparam-input.component.ts
+++ b/src/app/components/ngparam-input/ngparam-input.component.ts
@@ -10,7 +10,10 @@ import { GenericInputComponent } from "../generic-input/generic-input.component"
 
 @Component({
     selector: "ngparam-input",
-    templateUrl: "../generic-input/generic-input.component.html"
+    templateUrl: "../generic-input/generic-input.component.html",
+    styleUrls: [
+        "./ngparam-input.component.scss"
+    ]
 })
 export class NgParamInputComponent extends GenericInputComponent implements Observer, OnDestroy {
     /**
@@ -86,27 +89,6 @@ export class NgParamInputComponent extends GenericInputComponent implements Obse
         return { isValid: valid, message: msg };
     }
 
-    protected modelToUI(v: any): string {
-        return String(v);
-    }
-
-    protected validateUIValue(ui: string): { isValid: boolean, message: string } {
-        let valid = false;
-        let msg: string;
-
-        if (! isNumeric(ui)) {
-            msg = "Veuillez entrer une valeur numérique";
-        } else {
-            valid = true;
-        }
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected uiToModel(ui: string) {
-        return +ui;
-    }
-
     public update(sender: any, data: any): void {
         switch (data["action"]) {
             case "ngparamAfterValue":
diff --git a/src/app/components/param-computed/param-computed.component.html b/src/app/components/param-computed/param-computed.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..0751e36bf1dc93de19a451f8aefd886085dc6130
--- /dev/null
+++ b/src/app/components/param-computed/param-computed.component.html
@@ -0,0 +1,7 @@
+<!-- a fake input bound to nothing, for the sake of UI consistency -->
+<mat-form-field>
+    <input matInput disabled class="form-control" type="text" [ngModel]="infoText" [placeholder]="param.title">
+    <button *ngIf="isDicho" mat-icon-button class="param-computed-more" (click)="openDialog()">
+        <mat-icon>more_horiz</mat-icon>
+    </button>
+</mat-form-field>
diff --git a/src/app/components/param-computed/param-computed.component.scss b/src/app/components/param-computed/param-computed.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..100e77a8198ef0f0c71528da0bfa1d3cac9a97c9
--- /dev/null
+++ b/src/app/components/param-computed/param-computed.component.scss
@@ -0,0 +1,21 @@
+:host {
+    display: block;
+    margin-top: 14px;
+
+    mat-form-field {
+        width: calc(100% - 16px);
+        margin-right: 16px;
+
+        ::ng-deep input.mat-input-element {
+            line-height: 1.3em;
+            width: calc(100% - 40px);
+            text-overflow: ellipsis;
+        }
+
+        .param-computed-more {
+            position: absolute;
+            bottom: 0;
+            right: 0;
+        }
+    }
+}
diff --git a/src/app/components/param-computed/param-computed.component.ts b/src/app/components/param-computed/param-computed.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a1148d90b5b25d7256215e82e6509cde692791d2
--- /dev/null
+++ b/src/app/components/param-computed/param-computed.component.ts
@@ -0,0 +1,50 @@
+import { Component, Input } from "@angular/core";
+import { MatDialog } from "@angular/material";
+import { NgParameter } from "../../formulaire/ngparam";
+import { ParamCalculability } from "jalhyd";
+import { DialogEditParamComputedComponent } from "../dialog-edit-param-computed/dialog-edit-param-computed.component";
+
+@Component({
+    selector: "param-computed",
+    templateUrl: "./param-computed.component.html",
+    styleUrls: [
+        "./param-computed.component.scss"
+    ]
+})
+export class ParamComputedComponent {
+
+    @Input()
+    public param: NgParameter;
+
+    @Input()
+    public title: string;
+
+    constructor(
+        private editInitialValueDialog: MatDialog
+    ) { }
+
+    public get isDicho() {
+        return this.param.paramDefinition.calculability === ParamCalculability.DICHO;
+    }
+
+    public get infoText() {
+        let text = "En calcul (à traduire)"; // @TODO traduire "in calculation"
+        if (this.isDicho) {
+            text += " (valeur initiale: " + this.param.getValue() + ")";
+        }
+        return text;
+    }
+
+    public openDialog() {
+        // modification de la valeur initiale, sans avoir à remettre le mode de
+        // paramètre sur "fixé"
+        this.editInitialValueDialog.open(
+            DialogEditParamComputedComponent,
+            {
+                data: {
+                    param: this.param
+                }
+            }
+        );
+    }
+}
diff --git a/src/app/components/param-field-line/param-field-line.component.html b/src/app/components/param-field-line/param-field-line.component.html
index 41c5d1fead0e3e3405def7577ef33328870a42f3..90023511ea7da6245a770dee2e14cfe929fe8689 100644
--- a/src/app/components/param-field-line/param-field-line.component.html
+++ b/src/app/components/param-field-line/param-field-line.component.html
@@ -1,43 +1,49 @@
-<div class="row">
-    <!-- input de saisie de la valeur -->
-    <div [ngClass]="(formHasResults) ? 'col-xl-6 pt-3':'col-md-6 col-xl-8 pt-3'">
-        <ngparam-input [_inputDisabled]="isInputDisabled" [title]="title" (change)="onInputChange($event)"></ngparam-input>
-    </div>
 
-    <div class="btn-group col" role="group">
-        <!-- px-3 : padding left/right 3 -->
-        <!-- py-3 : padding top/bottom 3 -->
-        <!-- h-50 : hauteur relative de l'élément par rapport au parent à 50%-->
-        <!-- cf. https://getbootstrap.com/docs/4.0/utilities/spacing -->
+<div class="container" fxLayout="row wrap" fxLayoutAlign="space-between start">
+
+    <!-- input de saisie de la valeur -->
+    <div fxFlex="1 0 120px">
+        <!-- composant pour gérer le cas général (valeur numérique à saisir) -->
+        <ngparam-input [title]="param.title" [hidden]="! isRadioFixChecked" (change)="onInputChange($event)"></ngparam-input>
+ 
+        <!-- composant pour gérer le cas "paramètre calculé" -->
+        <param-computed *ngIf="isRadioCalChecked" [title]="title" [param]="param"></param-computed>
 
-        <!-- radio "fixé" -->
-        <label *ngIf="hasRadioFix()" class="{{radioFixClass}} h-75 px-3 py-3" [(ngModel)]="radioModel" mdbRadio="Left" name="radio_param_{{symbol}}"
-            value="fix" (click)="onRadioClick('fix')" [checked]=radioFixCheck [disabled]=isDisabled id="radio_fix">
-            {{ uitextParamFixe }}
-        </label>
+        <!-- composant pour gérer le cas "paramètre à varier" (min-max/liste de valeurs) -->
+        <param-values *ngIf="isRadioVarChecked" [title]="title" [param]="param" (valid)=onParamValuesValid($event)></param-values>
+    
+        <!-- composant pour gérer le cas "paramètre lié" -->
+        <param-link *ngIf="isRadioLinkChecked" [title]="title" [param]="param" (valid)=onParamValuesValid($event)></param-link>
+    </div>
 
-        <!-- radio "varier" -->
-        <label *ngIf="hasRadioVar()" class="{{radioVarClass}} h-75 px-3 py-3" [(ngModel)]="radioModel" mdbRadio="Middle" name="radio_param_{{symbol}}"
-            value="var" (click)="onRadioClick('var')" [checked]=radioVarCheck [disabled]=isDisabled id="radio_var">
-            {{ uitextParamVarier }}
-        </label>
+    <div class="toggle-group-container" fxFlex="0 0 auto">
+        <mat-button-toggle-group>
 
-        <!-- radio "calculer" -->
-        <label *ngIf="hasRadioCal()" class="{{radioCalClass}} h-75 px-3 py-3" [(ngModel)]="radioModel" mdbRadio="Right" name="radio_param_{{symbol}}"
-            value="cal" (click)="onRadioClick('cal')" [checked]=radioCalCheck [disabled]=isDisabled id="radio_cal">
-            {{ uitextParamCalculer }}
-        </label>
+            <mat-button-toggle id="radio_fix" value="radio_fix" *ngIf="hasRadioFix()"
+                (click)="onRadioClick('fix')" [checked]="isRadioFixChecked">
+                <span fxHide.xxs>{{ uitextParamFixe }}</span>
+                <span fxHide.gt-xxs>F</span>
+            </mat-button-toggle>
+        
+            <mat-button-toggle id="radio_var" value="radio_var" *ngIf="hasRadioVar()"
+                (click)="onRadioClick('var')" [checked]="isRadioVarChecked">
+                <span fxHide.xxs>{{ uitextParamVarier }}</span>
+                <span fxHide.gt-xxs>V</span>
+            </mat-button-toggle>
+        
+            <mat-button-toggle id="radio_cal" value="radio_cal" *ngIf="hasRadioCal()"
+                (click)="onRadioClick('cal')" [checked]="isRadioCalChecked">
+                <span fxHide.xxs>{{ uitextParamCalculer }}</span>
+                <span fxHide.gt-xxs>C</span>
+            </mat-button-toggle>
+        
+            <mat-button-toggle id="radio_link" value="radio_link" *ngIf="hasRadioLink()"
+                (click)="onRadioClick('link')" [checked]="isRadioLinkChecked">
+                <span fxHide.xxs>{{ uitextParamLie }}</span>
+                <span fxHide.gt-xxs>L</span>
+            </mat-button-toggle>
 
-        <!-- radio "lié" -->
-        <label *ngIf="hasRadioLink()" class="{{radioLinkClass}} h-75 px-3 py-3" [(ngModel)]="radioModel" mdbRadio="Right" name="radio_param_{{symbol}}"
-            value="link" (click)="onRadioClick('link')" [checked]=radioLinkCheck [disabled]=isDisabled id="radio_link">
-            {{ uitextParamLie }}
-        </label>
+        </mat-button-toggle-group>
     </div>
-</div>
 
-<!-- composant pour gérer le cas "paramètre à varier" (min-max/liste de valeurs) -->
-<param-values *ngIf="isRadioVarChecked" [param]="param" (valid)=onParamValuesValid($event)></param-values>
-
-<!-- composant pour gérer le cas "paramètre lié" -->
-<param-link *ngIf="isRadioLinkChecked" [param]="param" (valid)=onParamValuesValid($event)></param-link>
\ No newline at end of file
+</div>
diff --git a/src/app/components/param-field-line/param-field-line.component.scss b/src/app/components/param-field-line/param-field-line.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..4cfe3dad92ebfe608a3bef8eda844f0af394d3f4
--- /dev/null
+++ b/src/app/components/param-field-line/param-field-line.component.scss
@@ -0,0 +1,13 @@
+.toggle-group-container {
+    text-align: right;
+}
+
+mat-button-toggle-group {
+    margin-top: 4px;
+    box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12);
+
+    /*::ng-deep .mat-button-toggle-label-content {
+        line-height: 32px;
+        padding: 0 10px;
+    }*/
+}
diff --git a/src/app/components/param-field-line/param-field-line.component.ts b/src/app/components/param-field-line/param-field-line.component.ts
index 82608891a6bc58b9f5fe9135a6f89b7930ac7e66..50f368d0776457e65cc540d10e68bb22ea18d0ba 100644
--- a/src/app/components/param-field-line/param-field-line.component.ts
+++ b/src/app/components/param-field-line/param-field-line.component.ts
@@ -7,25 +7,16 @@ import { ServiceFactory } from "../../services/service-factory";
 import { ParamValueMode, CalculatorType, ParallelStructure } from "jalhyd";
 import { FormulaireService } from "../../services/formulaire/formulaire.service";
 import { ParamLinkComponent } from "../param-link/param-link.component";
+import { ParamComputedComponent } from "../param-computed/param-computed.component";
 
+/**
+ * Sélecteur de mode pour chaque paramètre: fixé, varier, calculer, lié
+ */
 @Component({
     selector: "param-field-line",
     templateUrl: "./param-field-line.component.html",
-    styles: [
-        `.btn-on {
-            color:#505050;
-            border: 3px solid #505050;
-            background-color:white;
-            text-transform: uppercase;
-            font-size: 0.8em;
-        }`,
-        `.btn-off {
-            color:white;
-            border: 3px solid #505050;
-            background-color:#505050;
-            text-transform: uppercase;
-            font-size: 0.8em;
-        }`
+    styleUrls: [
+        "./param-field-line.component.scss"
     ]
 })
 export class ParamFieldLineComponent implements OnChanges {
@@ -37,17 +28,6 @@ export class ParamFieldLineComponent implements OnChanges {
         this.inputChange = new EventEmitter();
     }
 
-    public get title(): string {
-        let t = "";
-        if (this.param.label !== undefined) {
-            t = this.param.label;
-        }
-        if (this.param.unit !== undefined && this.param.unit !== "") {
-            t = t + " (" + this.param.unit + ")";
-        }
-        return t;
-    }
-
     private get uitextParamFixe() {
         return this.intlService.localizeText("INFO_PARAMFIELD_PARAMFIXE");
     }
@@ -71,110 +51,36 @@ export class ParamFieldLineComponent implements OnChanges {
         return this.param.symbol;
     }
 
-    /**
-    * calcule l'état du radio "paramètre fixé"
-    */
+    // états des boutons pour l'interface
+
     private get radioFixCheck(): string {
         return this.isRadioFixChecked ? "checked" : undefined;
     }
-
-    /**
-    * calcule l'état du radio "paramètre à varier"
-    */
     private get radioVarCheck(): string {
         return this.isRadioVarChecked ? "checked" : undefined;
     }
-
-    /**
-    * calcule l'état du radio "paramètre à calculer"
-    */
     private get radioCalCheck(): string {
-        if (this.param.radioState === ParamRadioConfig.CAL) {
-            return "checked";
-        }
+        return this.isRadioCalChecked ? "checked" : undefined;
     }
-
-    /**
-    * calcule l'état du radio "paramètre lié"
-    */
     private get radioLinkCheck(): string {
-        if (this.param.radioState === ParamRadioConfig.LINK) {
-            return "checked";
-        }
+        return this.isRadioLinkChecked ? "checked" : undefined;
     }
 
-    /**
-     * retourne l'état du radio "paramètre fixé" sous forme booléenne
-     */
+    // états booléens des boutons
+
     public get isRadioFixChecked(): boolean {
         return this.param.radioState === ParamRadioConfig.FIX;
     }
-
-    /**
-     * retourne l'état du radio "paramètre à varier" sous forme booléenne
-     */
     public get isRadioVarChecked(): boolean {
         return this.param.radioState === ParamRadioConfig.VAR;
     }
-
-    /**
-     * retourne l'état du radio "paramètre lié" sous forme booléenne
-     */
+    public get isRadioCalChecked(): boolean {
+        return this.param.radioState === ParamRadioConfig.CAL;
+    }
     public get isRadioLinkChecked(): boolean {
         return this.param.radioState === ParamRadioConfig.LINK;
     }
 
-    /**
-     * désactivation de tous les boutons radio si paramètre par défaut à "CAL"
-     */
-    private get isDisabled(): boolean {
-        return this.param.isDefault && this.param.radioState === ParamRadioConfig.CAL;
-    }
-
-    /**
-     * désactivation du champ de saisie
-     */
-    public get isInputDisabled(): boolean {
-        return this.param.radioState !== ParamRadioConfig.FIX;
-    }
-
-    private get radioFixClass(): string {
-        if (this.on) {
-            return this.radioFixCheck ? this.onClass : this.offClass;
-        }
-        return "";
-    }
-
-    /**
-     * classe du radio "varier"
-     */
-    private get radioVarClass(): string {
-        if (this.on) {
-            return this.radioVarCheck ? this.onClass : this.offClass;
-        }
-        return "";
-    }
-
-    /**
-     * classe du radio "calculer"
-     */
-    private get radioCalClass(): string {
-        if (this.on) {
-            return this.radioCalCheck ? this.onClass : this.offClass;
-        }
-        return "";
-    }
-
-    /**
-     * classe du radio "lié"
-     */
-    private get radioLinkClass(): string {
-        if (this.on) {
-            return this.radioLinkCheck ? this.onClass : this.offClass;
-        }
-        return "";
-    }
-
     /**
      * validité des saisies du composant
      */
@@ -186,6 +92,10 @@ export class ParamFieldLineComponent implements OnChanges {
             case ParamRadioConfig.VAR:
                 return this._isRangeValid;
 
+            case ParamRadioConfig.LINK:
+                // at first this._paramLinkComponent is undefined until view is refreshed
+                return this._paramLinkComponent ? this._paramLinkComponent.isValid : true;
+
             default:
                 return true;
         }
@@ -196,11 +106,14 @@ export class ParamFieldLineComponent implements OnChanges {
     }
 
     @Input()
-    private param: NgParameter;
+    public param: NgParameter;
 
     @ViewChild(NgParamInputComponent)
     private _ngParamInputComponent: NgParamInputComponent;
 
+    @ViewChild(ParamComputedComponent)
+    private _computedParamComponent: ParamComputedComponent;
+
     @ViewChild(ParamLinkComponent)
     private _paramLinkComponent: ParamLinkComponent;
 
@@ -210,14 +123,10 @@ export class ParamFieldLineComponent implements OnChanges {
     @Output()
     private inputChange: EventEmitter<void>;
 
-    /**
-     * true si la valeur saisie est valide
-     */
+    /** true si la valeur saisie est valide */
     private _isInputValid = false;
 
-    /**
-     * true si le min-max/liste est valide
-     */
+    /** true si le min-max/liste est valide */
     private _isRangeValid = true;
 
     private intlService: I18nService;
@@ -229,7 +138,6 @@ export class ParamFieldLineComponent implements OnChanges {
      * envoi d'un message au composant parent
      * cf. https://angular.io/guide/component-interaction#parent-listens-for-child-event
      */
-
     @Output()
     private radio = new EventEmitter<any>();
 
@@ -246,7 +154,7 @@ export class ParamFieldLineComponent implements OnChanges {
     public hasRadioFix(): boolean {
         switch (this.param.radioConfig) {
             case ParamRadioConfig.FIX:
-                return this.hasRadioLink();
+                return this.hasRadioLink(); // gné ?
 
             default:
                 return true;
@@ -304,16 +212,19 @@ export class ParamFieldLineComponent implements OnChanges {
 
     private onRadioClick(option: string) {
         const oldValue = this.param.valueMode;
-
         switch (option) {
             case "fix":
-                const oldValueMode = this.param.valueMode;
                 this.param.valueMode = ParamValueMode.SINGLE;
+                // @WTF why do we reset the value here ?
                 this.param.setValue(this, this.param.paramDefinition.paramValues.singleValue);
                 break;
 
             case "var":
-                this.param.valueMode = ParamValueMode.MINMAX; // min/max par défaut
+                // prevent setting LISTE mode back to MINMAX if someone clicks "variable" again
+                // after setting variable mode to LISTE
+                if (oldValue !== ParamValueMode.MINMAX && oldValue !== ParamValueMode.LISTE) {
+                    this.param.valueMode = ParamValueMode.MINMAX; // min/max par défaut
+                }
                 break;
 
             case "cal":
@@ -324,12 +235,10 @@ export class ParamFieldLineComponent implements OnChanges {
                 this.param.valueMode = ParamValueMode.LINK;
                 break;
         }
-
         this.radio.emit({
             "param": this.param,
             "oldValueMode": oldValue
         });
-
         // MAJ validité
         this.emitValidity();
     }
diff --git a/src/app/components/param-link/param-link.component.html b/src/app/components/param-link/param-link.component.html
index f7289b7393df1263526546072753c8c72459fd87..5b198816bf96e4b7b3df1aec2f428f7e681d1ec2 100644
--- a/src/app/components/param-link/param-link.component.html
+++ b/src/app/components/param-link/param-link.component.html
@@ -1,13 +1,10 @@
-<div class="row">
-    <div class="btn-group col-6 col-sm-3" dropdown (click)="onSelectLinkableParam($event)">
-        <button dropdownToggle class="btn btn-primary dropdown-toggle waves-light my-1" type="button" mdbRippleRadius>
-            {{ currentLinkedParamLabel }}
-        </button>
-        <div class="dropdown-menu">
-            <a class="dropdown-item" *ngFor="let e of linkableParams" [value]=e>{{ selectItemLabel(e) }}</a>
-        </div>
-    </div>
-    <div class="col-6 text-danger">
+<mat-form-field>
+    <mat-select [name]='"linked-param_" + param.uid' required [placeholder]="param.title" [(ngModel)]="currentLinkedParam">
+        <mat-option *ngFor="let e of linkableParams" [value]="e">
+            {{ selectItemLabel(e) }}
+        </mat-option>
+    </mat-select>
+    <mat-error>
         {{ message }}
-    </div>
-</div>
\ No newline at end of file
+    </mat-error>
+</mat-form-field>
diff --git a/src/app/components/param-link/param-link.component.scss b/src/app/components/param-link/param-link.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..d4cfda5d9b29ae602ee12716a9cd6f4a7af1c574
--- /dev/null
+++ b/src/app/components/param-link/param-link.component.scss
@@ -0,0 +1,23 @@
+:host {
+    display: block;
+    // width: 100%;
+    // width: 70%; min-width: 264px; // for smallest screens (360)
+    // get the select closer to the related param line above
+    margin-top: 14px;
+
+    mat-form-field {
+        width: calc(100% - 16px);
+        margin-right: 16px;
+
+        mat-select {
+    
+            ::ng-deep .mat-select-value {
+                > span {
+                    > span {
+                        line-height: 1.3em;
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/app/components/param-link/param-link.component.ts b/src/app/components/param-link/param-link.component.ts
index b0246dee55b77439a1e2c7b1e2f293a08e7a3d5d..4add3db0526ea2e31dee6f436bab280a8b119f2b 100644
--- a/src/app/components/param-link/param-link.component.ts
+++ b/src/app/components/param-link/param-link.component.ts
@@ -7,12 +7,18 @@ import { FormulaireService } from "../../services/formulaire/formulaire.service"
 
 @Component({
     selector: "param-link",
-    templateUrl: "./param-link.component.html"
+    templateUrl: "./param-link.component.html",
+    styleUrls: [
+        "./param-link.component.scss"
+    ]
 })
 export class ParamLinkComponent implements OnChanges, Observer, OnDestroy {
     // paramètre géré (qui sera lié à une valeur, cad qui importe cette valeur)
     @Input()
-    private param: NgParameter;
+    public param: NgParameter;
+
+    @Input()
+    public title: string;
 
     @Output()
     private valid: EventEmitter<boolean>;
@@ -47,6 +53,8 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy {
 
     private _formService: FormulaireService;
 
+    public label = "Choix du paramètre lié (à traduire)";
+
     constructor() {
         this.valid = new EventEmitter();
         this._formService = ServiceFactory.instance.formulaireService;
@@ -61,27 +69,21 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy {
         return this._message;
     }
 
-    /**
-     * envoi d'un événement de validité
-     */
-    private emitValidity() {
-        // this.valid.emit(this._validList);
+    public set currentLinkedParam(p: any) {
+        for (let i = 0; i < this._linkableParams.length; i++) {
+            if (this._linkableParams[i].value.uid === p.uid) {
+                this.linkTo(i);
+                break;
+            } else {
+                i++;
+            }
+        }
     }
 
-    /**
-     * réception d'un événement du menu des paramètres liables
-     */
-    public onSelectLinkableParam(event: any) {
-        const next = event.target.value;
-        if (next !== undefined && next !== "") { // opening the dropdown returns ""
-            let i = 0;
-            for (const e of this._linkableParams) {
-                if (this._linkableParams[i].value.uid === next.value.uid) {
-                    this.linkTo(i);
-                    break;
-                } else {
-                    i++;
-                }
+    public get currentLinkedParam() {
+        if (this._linkableParams !== undefined) {
+            if (this._currentIndex !== -1 && this._currentIndex < this._linkableParams.length) {
+                return this._linkableParams[this._currentIndex];
             }
         }
     }
@@ -90,13 +92,16 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy {
      * valeur courante affichée dans le select des paramètres liables
      */
     public get currentLinkedParamLabel(): string {
-        if (this._linkableParams !== undefined) {
-            if (this._currentIndex !== -1 && this._currentIndex < this._linkableParams.length) {
-                return this.selectItemLabel(this._linkableParams[this._currentIndex]);
-            }
+        const clp = this.currentLinkedParam();
+        if (clp) {
+            return this.selectItemLabel(clp);
         }
     }
 
+    public get isValid(): boolean {
+        return this._currentIndex !== -1 && this.param.isValid;
+    }
+
     /**
      * attribut "label" d'une entrée du select des paramètres
      */
diff --git a/src/app/components/param-values/ngparam-max.component.ts b/src/app/components/param-values/ngparam-max.component.ts
deleted file mode 100644
index d878dd68b385f2e02267ce9eff4d3d67dc34cb51..0000000000000000000000000000000000000000
--- a/src/app/components/param-values/ngparam-max.component.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-import { Component, Input, ChangeDetectorRef } from "@angular/core";
-
-import { isNumeric } from "jalhyd";
-
-import { GenericInputComponent } from "../generic-input/generic-input.component";
-import { I18nService } from "../../services/internationalisation/internationalisation.service";
-import { NgParameter } from "../../formulaire/ngparam";
-
-@Component({
-    selector: "ngparam-max",
-    templateUrl: "../generic-input/generic-input.component.html"
-})
-export class NgParamMaxComponent extends GenericInputComponent {
-    constructor(private intlService: I18nService, cdRef: ChangeDetectorRef) {
-        super(cdRef);
-    }
-
-    /**
-     * paramètre géré
-     */
-    private get _param(): NgParameter {
-        return this._model;
-    }
-
-    protected getModelValue(): any {
-        if (this._param) {
-            return this._param.maxValue;
-        }
-    }
-
-    protected setModelValue(sender: any, v: any) {
-        this._param.maxValue = v;
-    }
-
-    protected validateModelValue(v: any): { isValid: boolean, message: string } {
-        let msg;
-        let valid = false;
-
-        if (this._param === undefined) {
-            msg = "internal error, model undefined";
-        } else {
-            if (!this._param.checkMax(v)) {
-                msg = "La valeur n'est pas dans ]" + this._param.minValue + " , " + this._param.domain.maxValue + "]";
-            } else {
-                valid = true;
-            }
-        }
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected modelToUI(v: any): string {
-        if (typeof (v) === "number") {
-            return String(v);
-        }
-    }
-
-    protected validateUIValue(ui: string): { isValid: boolean, message: string } {
-        let valid = false;
-        let msg: string;
-
-        if (! isNumeric(ui)) {
-            msg = "Veuillez entrer une valeur numérique";
-        } else {
-            valid = true;
-        }
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected uiToModel(ui: string) {
-        return +ui;
-    }
-}
diff --git a/src/app/components/param-values/ngparam-min.component.ts b/src/app/components/param-values/ngparam-min.component.ts
deleted file mode 100644
index 687df8d0ecefb457a1456af08cc45b9d1c1ccc84..0000000000000000000000000000000000000000
--- a/src/app/components/param-values/ngparam-min.component.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-import { Component, Input, ChangeDetectorRef } from "@angular/core";
-
-import { isNumeric } from "jalhyd";
-
-import { GenericInputComponent } from "../generic-input/generic-input.component";
-import { I18nService } from "../../services/internationalisation/internationalisation.service";
-import { NgParameter } from "../../formulaire/ngparam";
-
-@Component({
-    selector: "ngparam-min",
-    templateUrl: "../generic-input/generic-input.component.html"
-})
-export class NgParamMinComponent extends GenericInputComponent {
-    constructor(private intlService: I18nService, cdRef: ChangeDetectorRef) {
-        super(cdRef);
-    }
-
-    /**
-     * paramètre géré
-     */
-    private get _param(): NgParameter {
-        return this._model;
-    }
-
-    protected getModelValue(): any {
-        if (this._param) {
-            return this._param.minValue;
-        }
-    }
-
-    protected setModelValue(sender: any, v: any) {
-        this._param.minValue = v;
-    }
-
-    protected validateModelValue(v: any): { isValid: boolean, message: string } {
-        let msg: string;
-        let valid = false;
-
-        if (this._param === undefined) {
-            msg = "internal error, model undefined";
-        } else {
-            if (!this._param.checkMin(v)) {
-                msg = "La valeur n'est pas dans [" + this._param.domain.minValue + " , " + this._param.maxValue + "[";
-            } else {
-                valid = true;
-            }
-        }
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected modelToUI(v: any): string {
-        if (typeof (v) === "number") {
-            return String(v);
-        }
-    }
-
-    protected validateUIValue(ui: string): { isValid: boolean, message: string } {
-        let valid = false;
-        let msg: string;
-
-        if (! isNumeric(ui)) {
-            msg = "Veuillez entrer une valeur numérique";
-        } else {
-            valid = true;
-        }
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected uiToModel(ui: string): any {
-        return +ui;
-    }
-}
diff --git a/src/app/components/param-values/ngparam-step.component.ts b/src/app/components/param-values/ngparam-step.component.ts
deleted file mode 100644
index ac0fd27fd70cacf6071ed5ff0dedac9a1c3d5db0..0000000000000000000000000000000000000000
--- a/src/app/components/param-values/ngparam-step.component.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-import { Component, Input, ChangeDetectorRef } from "@angular/core";
-
-import { isNumeric } from "jalhyd";
-
-import { GenericInputComponent } from "../generic-input/generic-input.component";
-import { I18nService } from "../../services/internationalisation/internationalisation.service";
-import { NgParameter } from "../../formulaire/ngparam";
-
-@Component({
-    selector: "ngparam-step",
-    templateUrl: "../generic-input/generic-input.component.html"
-})
-export class NgParamStepComponent extends GenericInputComponent {
-    constructor(private intlService: I18nService, cdRef: ChangeDetectorRef) {
-        super(cdRef);
-    }
-
-    /**
-     * paramètre géré
-     */
-    private get _param(): NgParameter {
-        return this._model;
-    }
-
-    protected getModelValue(): any {
-        if (this._param) {
-            return this._param.stepValue;
-        }
-    }
-
-    protected setModelValue(sender: any, v: any) {
-        this._param.stepValue = v;
-    }
-
-    protected validateModelValue(v: any): { isValid: boolean, message: string } {
-        let msg: string;
-        let valid = false;
-
-        if (! this._param) {
-            msg = "internal error, model undefined";
-        } else {
-            if (this._param.isMinMaxValid) {
-                if (!this._param.checkStep(v)) {
-                    msg = "La valeur n'est pas dans " + this._param.stepRefValue.toString();
-                } else {
-                    valid = v > 0;
-                    if (!valid) {
-                        msg = "La valeur ne peut pas être <= 0";
-                    }
-                }
-            } else {
-                msg = "Veuillez corriger le min/max";
-            }
-        }
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected modelToUI(v: any): string {
-        if (typeof (v) === "number") {
-            return String(v);
-        }
-        return "<invalid>";
-    }
-
-    protected validateUIValue(ui: string): { isValid: boolean, message: string } {
-        let valid = false;
-        let msg: string;
-
-        if (! isNumeric(ui)) {
-            msg = "Veuillez entrer une valeur numérique";
-        } else {
-            valid = true;
-        }
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected uiToModel(ui: string) {
-        return +ui;
-    }
-}
diff --git a/src/app/components/param-values/param-values.component.html b/src/app/components/param-values/param-values.component.html
index f4dc496878e94c63d55d6e5e1b59bc277430f16d..37cda318bfbb8ac4581d6f45ab2188656e7f89fd 100644
--- a/src/app/components/param-values/param-values.component.html
+++ b/src/app/components/param-values/param-values.component.html
@@ -1,24 +1,7 @@
-<div class="row">
-    <div class="btn-group col-12 col-sm-3" dropdown (click)="onSelectValueMode($event)">
-        <button dropdownToggle class="btn btn-primary dropdown-toggle waves-light my-1" type="button" mdbRippleRadius>
-            {{ currentModeSelectLabel }}
-        </button>
-        <div class="dropdown-menu">
-            <a class="dropdown-item" *ngFor="let e of valueModes" [value]=e.value>{{ e.label }}</a>
-        </div>
-    </div>
-
-    <div *ngIf="isMinMax" class="col-12 col-sm-3">
-        <ngparam-min [title]="uitextValeurMini" (onChange)="onMinChanged($event)"></ngparam-min>
-    </div>
-    <div *ngIf="isMinMax" class="col-12 col-sm-3">
-        <ngparam-max [title]="uitextValeurMaxi" (onChange)="onMaxChanged($event)"></ngparam-max>
-    </div>
-    <div *ngIf="isMinMax" class="col-12 col-sm-3">
-        <ngparam-step [title]="uitextPasVariation" [param]="param" (onChange)="onStepChanged($event)"></ngparam-step>
-    </div>
-
-    <div *ngIf="isList" class="col-12 col-sm-6">
-        <value-list title="valeurs séparées par ';'" (onChange)="onListChanged($event)"></value-list>
-    </div>
-</div>
\ No newline at end of file
+<!-- a fake input bound to nothing, for the sake of UI consistency -->
+<mat-form-field>
+    <input matInput disabled class="form-control" type="text" [ngModel]="infoText" [placeholder]="param.title">
+    <button mat-icon-button class="param-values-more" (click)="openDialog()">
+        <mat-icon>more_horiz</mat-icon>
+    </button>
+</mat-form-field>
diff --git a/src/app/components/param-values/param-values.component.scss b/src/app/components/param-values/param-values.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..720b9b04ac2709bcc2d7f0e948b5f725b7596afb
--- /dev/null
+++ b/src/app/components/param-values/param-values.component.scss
@@ -0,0 +1,21 @@
+:host {
+    display: block;
+    margin-top: 14px;
+
+    mat-form-field {
+        width: calc(100% - 16px);
+        margin-right: 16px;
+
+        ::ng-deep input.mat-input-element {
+            line-height: 1.3em;
+            width: calc(100% - 40px);
+            text-overflow: ellipsis;
+        }
+
+        .param-values-more {
+            position: absolute;
+            bottom: 0;
+            right: 0;
+        }
+    }
+}
diff --git a/src/app/components/param-values/param-values.component.ts b/src/app/components/param-values/param-values.component.ts
index d7f2b4b57a7ff4176a9398a5ef785e675cfdd902..221442da9c54f1aff8b4c1d63e69cca553ffcee9 100644
--- a/src/app/components/param-values/param-values.component.ts
+++ b/src/app/components/param-values/param-values.component.ts
@@ -1,361 +1,67 @@
-import { Component, Input, Output, EventEmitter, ViewChild, AfterViewChecked, OnChanges } from "@angular/core";
-
-import { ParamValueMode } from "jalhyd";
-
-import { I18nService } from "../../services/internationalisation/internationalisation.service";
+import { Component, Input, AfterViewInit } from "@angular/core";
 import { NgParameter } from "../../formulaire/ngparam";
-import { NgParamMinComponent } from "./ngparam-min.component";
-import { NgParamMaxComponent } from "./ngparam-max.component";
-import { NgParamStepComponent } from "./ngparam-step.component";
-import { BaseComponent } from "../base/base.component";
-import { ValueListComponent } from "./value-list.component";
+import { DialogEditParamValuesComponent } from "../dialog-edit-param-values/dialog-edit-param-values.component";
+import { MatDialog } from "@angular/material";
+import { ParamValueMode } from "jalhyd";
 
 @Component({
     selector: "param-values",
     templateUrl: "./param-values.component.html",
-    styles: [
-        `.btn-on {
-            color:#505050;
-            border: 3px solid #505050;
-            background-color:white;
-            text-transform: uppercase;
-            font-size: 0.8em;
-        }`,
-        `.btn-off {
-            color:white;
-            border: 3px solid #505050;
-            background-color:#505050;
-            text-transform: uppercase;
-            font-size: 0.8em;
-        }`
+    styleUrls: [
+        "./param-values.component.scss"
     ]
 })
-export class ParamValuesComponent extends BaseComponent implements AfterViewChecked, OnChanges {
-    @Input()
-    private param: NgParameter;
-
-    private _valueModes = [];
-
-    /**
-     * true quand les champs du composant et de ses enfants sont initialisés
-     */
-    private _initCompleted = false;
-
-    /**
-     * true si la valeur min est valide
-     */
-    private _validMin = false;
-
-    /**
-     * true si la valeur max est valide
-     */
-    private _validMax = false;
-
-    /**
-     * true si la valeur du pas est valide
-     */
-    private _validStep = false;
-
-    /**
-     * true si la liste de valeurs est valide
-     */
-    private _validList = false;
-
-    /**
-     * flag signalant qu'il faut initialiser le composant ValueListComponent (par ex quand on a sélectionné le mode "liste")
-     */
-    private _doInitList = false;
-
-    /**
-     * flag signalant qu'il faut initialiser les composants min/max/pas
-     */
-    private _doInitMinmax = false;
-
-    /**
-     * composant de saisie du minimum
-     */
-    @ViewChild(NgParamMinComponent)
-    private _minComponent: NgParamMinComponent;
-
-    /**
-     * composant de saisie du maximum
-     */
-    @ViewChild(NgParamMaxComponent)
-    private _maxComponent: NgParamMaxComponent;
-
-    /**
-     * composant de saisie du pas de variation
-     */
-    @ViewChild(NgParamStepComponent)
-    private _stepComponent: NgParamStepComponent;
-
-    /**
-     * composant de saisie d'une liste de valeurs
-     */
-    @ViewChild(ValueListComponent)
-    private _listComponent: ValueListComponent;
-
-    @Output()
-    private valid: EventEmitter<boolean>;
-
-    constructor(private intlService: I18nService) {
-        super();
-        this._valueModes.push({ "value": ParamValueMode.MINMAX, "label": "Min/max" });
-        this._valueModes.push({ "value": ParamValueMode.LISTE, "label": "Liste" });
-        this.valid = new EventEmitter();
-    }
-
-    /**
-     * init des champs min/max/pas
-     */
-    private initMinMaxStep() {
-        if (this.isMinMax && this._doInitMinmax) {
-            this._doInitMinmax = false;
-
-            // valeur pour min : celle déjà définie ou celle déduite de la valeur saisie
-            let min: number = this.param.minValue;
-            if (min === undefined) {
-                min = this.param.getValue() / 2;
-            }
-
-            // valeur pour max : celle déjà définie ou celle déduite de la valeur saisie
-            let max: number = this.param.maxValue;
-            if (max === undefined) {
-                max = this.param.getValue() * 2;
-            }
-
-            this.param.minValue = min;
-            this._minComponent.model = this.param;
-
-            this.param.maxValue = max;
-            this._maxComponent.model = this.param;
-
-            // valeur du pas
-            let step = this.param.stepValue;
-            if (step === undefined) {
-                step = (max - min) / 20;
-            }
-            this.param.stepValue = step;
-            this._stepComponent.model = this.param;
-
-            this.validateAll();
-
-            this._validMin = this._minComponent.isValid;
-            this._validMax = this._maxComponent.isValid;
-            this._validStep = this._stepComponent.isValid;
-            this.emitValidity();
-
-            this._initCompleted = true;
-        }
-    }
-
-    /**
-     * initialisation de la liste de valeurs avec celle du paramètre géré
-     */
-    private initList() {
-        if (this._doInitList && this._listComponent !== undefined) {
-            this._doInitList = false;
-            let l = this.param.valueList;
-            if (l === undefined) {
-                if (this.param.isDefined) {
-                    l = [this.param.getValue()];
-                } else {
-                    l = [];
-                }
-            }
-
-            this.param.valueList = l;
-            this._listComponent.model = this.param;
-        }
-    }
-
-    /**
-     * revalidation de tous les composants enfants
-     */
-    private validateAll() {
-        if (this._minComponent !== undefined) {
-            this._minComponent.validate();
-        }
-        if (this._maxComponent !== undefined) {
-            this._maxComponent.validate();
-        }
-        if (this._stepComponent !== undefined) {
-            this._stepComponent.validate();
-        }
-        if (this._listComponent !== undefined) {
-            this._listComponent.validate();
-        }
-    }
+export class ParamValuesComponent implements AfterViewInit {
 
-    /**
-     * envoi d'un événement de validité
-     */
-    private emitValidity() {
-        switch (this.param.valueMode) {
-            case ParamValueMode.LISTE:
-                this.valid.emit(this._validList);
-                break;
-
-            case ParamValueMode.MINMAX:
-                this.valid.emit(this._validMin && this._validMax && this._validStep);
-                break;
-        }
-    }
-
-    /**
-     * réception d'un événement de NgParamMinComponent
-     */
-    private onMinChanged(event: any) {
-        if (this._initCompleted) {
-            switch (event.action) {
-                case "model":
-                    this.validateAll();
-                    break;
-
-                case "valid":
-                    this._validMin = event.value;
-                    this.emitValidity();
-                    break;
-            }
-        }
-    }
-
-    /**
-     * réception d'un événement de NgParamMaxComponent
-     */
-    private onMaxChanged(event: any) {
-        if (this._initCompleted) {
-            switch (event.action) {
-                case "model":
-                    this.validateAll();
-                    break;
-
-                case "valid":
-                    this._validMax = event.value;
-                    this.emitValidity();
-                    break;
-            }
-        }
-    }
-
-    /**
-     * réception d'un événement de NgParamStepComponent
-     */
-    private onStepChanged(event: any) {
-        if (this._initCompleted) {
-            switch (event.action) {
-                case "model":
-                    this.validateAll();
-                    break;
-
-                case "valid":
-                    this._validStep = event.value;
-                    this.emitValidity();
-                    break;
-            }
-        }
-    }
-
-    /**
-     * réception d'un événement de ValueListComponent
-     */
-    private onListChanged(event: any) {
-        if (this._initCompleted) {
-            switch (event.action) {
-                case "model":
-                    this.validateAll();
-                    break;
-
-                case "valid":
-                    this._validList = event.value;
-                    this.emitValidity();
-                    break;
-            }
-        }
-    }
-
-    private get uitextValeurMini() {
-        return this.intlService.localizeText("INFO_PARAMFIELD_VALEURMINI");
-    }
-
-    private get uitextValeurMaxi() {
-        return this.intlService.localizeText("INFO_PARAMFIELD_VALEURMAXI");
-    }
+    @Input()
+    public param: NgParameter;
 
-    private get uitextPasVariation() {
-        return this.intlService.localizeText("INFO_PARAMFIELD_PASVARIATION");
-    }
+    @Input()
+    public title: string;
 
-    /**
-     * true si mode "liste de valeurs"
-     */
-    public get isList(): boolean {
-        return this.param.valueMode === ParamValueMode.LISTE;
-    }
 
-    /**
-     * true si mode "lié"
-     */
-    public get isLink(): boolean {
-        return this.param.valueMode === ParamValueMode.LINK;
-    }
+    constructor(
+        private editValuesDialog: MatDialog
+    ) { }
 
-    /**
-     * true si mode "min/max/pas"
-     */
-    public get isMinMax(): boolean {
+    public get isMinMax() {
         return this.param.valueMode === ParamValueMode.MINMAX;
     }
 
-    /**
-     * valeur courante affichée dans le select min-max/list
-     */
-    public get currentModeSelectLabel(): string {
-        return ParamValueMode[this.param.valueMode];
+    public get isListe() {
+        return this.param.valueMode === ParamValueMode.LISTE;
     }
 
-    /**
-     * réception d'un événement du menu "min/max/liste"
-     */
-    public onSelectValueMode(event: any) {
-        const next = event.target.value;
-
-        switch (next) {
-            // on a sélectionné "min/max" ?
-            case ParamValueMode.MINMAX:
-                this._doInitMinmax = true;
-                break;
-
-            // on a sélectionné "liste" ?
-            case ParamValueMode.LISTE:
-                this._doInitList = true;
-                break;
-
-            default:
-                throw new Error("valeur " + next + " de ParamValueMode non prise en charge");
+    public get infoText() {
+        let text = "(à traduire) ";
+        if (this.isMinMax) {
+            text += "min: " + this.param.minValue + ", max: " + this.param.maxValue + ", step: " + this.param.stepValue;
+        } else if (this.isListe) {
+            const vals = this.param.valueList || [];
+            console.log("VALS", vals, this.param.valueList);
+            text += "values: " + vals.slice(0, 20).join(";");
         }
-
-        this.param.valueMode = next;
+        return text;
     }
 
-    public get valueModes() {
-        return this._valueModes;
-    }
-
-    /**
-     * appelé quand les @Input changent
-     */
-    ngOnChanges() {
-        if (this.isMinMax) {
-            this._doInitMinmax = true;
-        } else {
-            this._doInitList = true;
-        }
+    public openDialog() {
+        // modification de la valeur initiale, sans avoir à remettre le mode de
+        // paramètre sur "fixé"
+        this.editValuesDialog.open(
+            DialogEditParamValuesComponent,
+            {
+                data: {
+                    param: this.param
+                }
+            }
+        );
     }
 
-    ngAfterViewChecked() {
-        super.ngAfterViewChecked();
-        this.initMinMaxStep();
-        this.initList();
+    public ngAfterViewInit() {
+        // use Promise trick to introduce a pseudo-timeout and avoid ExpressionChangedAfterItHasBeenCheckedError
+        Promise.resolve().then(() => {
+            // open dialog when switching to this mode, to ease the setup
+            this.openDialog();
+        });
     }
 }
diff --git a/src/app/components/param-values/value-list.component.ts b/src/app/components/param-values/value-list.component.ts
deleted file mode 100644
index fa2fd379e9008245a8a5058d98b8d2cf0cfc0e51..0000000000000000000000000000000000000000
--- a/src/app/components/param-values/value-list.component.ts
+++ /dev/null
@@ -1,106 +0,0 @@
-import { Component, Input, ChangeDetectorRef } from "@angular/core";
-
-import { GenericInputComponent } from "../generic-input/generic-input.component";
-import { I18nService } from "../../services/internationalisation/internationalisation.service";
-import { NgParameter } from "../../formulaire/ngparam";
-import { Message } from "jalhyd";
-
-@Component({
-    selector: "value-list",
-    templateUrl: "../generic-input/generic-input.component.html"
-})
-export class ValueListComponent extends GenericInputComponent {
-    constructor(private intlService: I18nService, cdRef: ChangeDetectorRef) {
-        super(cdRef);
-    }
-
-    /**
-     * paramètre géré
-     */
-    private get _param(): NgParameter {
-        return this._model;
-    }
-
-    protected getModelValue(): any {
-        if (! this._param) {
-            return this._param.valueList;
-        }
-    }
-
-    protected setModelValue(sender: any, l: any) {
-        if (typeof (l) === "number") {
-            this._param.valueList = [];
-            this._param.valueList.push(l);
-        } else {
-            this._param.valueList = l;
-        }
-    }
-
-    protected validateModelValue(v: any): { isValid: boolean, message: string } {
-        let msg;
-        let valid = false;
-
-        if (v instanceof Array) {
-            valid = true;
-            try {
-                this._param.checkList(v);
-            } catch (ex) {
-                valid = false;
-                if (ex instanceof Message) {
-                    msg = this.intlService.localizeMessage(ex);
-                } else {
-                    msg = "invalid value";
-                }
-            }
-        } else {
-            msg = "Veuillez entrer une liste de nombres";
-        }
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected modelToUI(v: any): string {
-        let res = "";
-        if (v !== undefined && v !== null) {
-            for (const e of v) {
-                if (res !== "") {
-                    res += ";";
-                }
-                res += String(e);
-            }
-        }
-        return res;
-    }
-
-    protected validateUIValue(ui: string): { isValid: boolean, message: string } {
-        let valid = false;
-        let msg: string;
-
-        const tmp: string[] = ui.split(";");
-        let res = true;
-        for (const v of tmp) {
-            const isnum = v !== "" && (+v === +v);
-            res = res && isnum;
-            if (!res) {
-                break;
-            }
-        }
-
-        if (!res) {
-            msg = "Veuillez entrer une liste de nombres";
-        } else {
-            valid = true;
-        }
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected uiToModel(ui: string) {
-        const tmp: string[] = ui.split(";");
-        const res = [];
-        for (const v of tmp) {
-            res.push(+v);
-        }
-        return res;
-    }
-}
diff --git a/src/app/components/result-element/horizontal-result-element.component.html b/src/app/components/result-element/horizontal-result-element.component.html
index 18f7cf7daa2f166d843325843431db5a18969041..41b4809b41b239fb51ff04a159ababa87d42c722 100644
--- a/src/app/components/result-element/horizontal-result-element.component.html
+++ b/src/app/components/result-element/horizontal-result-element.component.html
@@ -21,10 +21,11 @@
 -->
 
 <!-- icône en cas d'erreur -->
-<mat-icon *ngIf="hasError" style="color:red" aria-hidden="true" [mdbTooltip]="popTemplate" [isDisabled]="tooltipDisabled">warning</mat-icon>
+<mat-icon *ngIf="hasError" style="color:red" aria-hidden="true" [isDisabled]="tooltipDisabled">warning</mat-icon>
 
 <!-- valeur  -->
-<span *ngIf="!hasError" [mdbTooltip]="popTemplate" [isDisabled]="tooltipDisabled">
+<!-- [mdbTooltip]="popTemplate" -->
+<span *ngIf="!hasError" [isDisabled]="tooltipDisabled">
     {{ resultValue }}
 </span>
 
diff --git a/src/app/components/result-element/vertical-result-element.component.html b/src/app/components/result-element/vertical-result-element.component.html
index 48886d327307acf4c2ce34fd9387350eb6c3901b..fa199fa1da492104e98b91bd1de8f8395dbd25fa 100644
--- a/src/app/components/result-element/vertical-result-element.component.html
+++ b/src/app/components/result-element/vertical-result-element.component.html
@@ -7,7 +7,8 @@
     {{ resultLabel }}
 </td>
 
-<td *ngIf="hasValue||hasError" [mdbTooltip]="popTemplate" [isDisabled]="tooltipDisabled" class="value2">
+<!-- [mdbTooltip]="popTemplate" -->
+<td *ngIf="hasValue||hasError" [isDisabled]="tooltipDisabled" class="value2">
     <!-- icône en cas d'erreur -->
     <mat-icon *ngIf="hasError" style="color:red" aria-hidden="true">warning</mat-icon>
 
diff --git a/src/app/components/results-graph/graph-type.component.ts b/src/app/components/results-graph/graph-type.component.ts
index 5ccfbf764b89e8dec1ff38b08a42e52380908f52..c3d41ebd9265b96f9183f1a7edf26ab6a90b85cc 100644
--- a/src/app/components/results-graph/graph-type.component.ts
+++ b/src/app/components/results-graph/graph-type.component.ts
@@ -1,23 +1,21 @@
 import { Component } from "@angular/core";
-
 import { Observable, IObservable, Observer } from "jalhyd";
-
-import { GenericSelectComponent } from "../generic-select/generic-select.component";
 import { GraphType } from "../../results/var-results";
 
 @Component({
     selector: "graph-type",
     templateUrl: "../generic-select/generic-select.component.html"
 })
-export class GraphTypeSelectComponent extends GenericSelectComponent<GraphType> implements IObservable {
+export class GraphTypeSelectComponent implements IObservable {
     private _entries: GraphType[] = [GraphType.Histogram, GraphType.Scatter];
     private _entriesLabels: string[] = ["Histogramme", "XY"];
     private _selected: GraphType;
 
     private _observable: Observable;
 
+    public label = "Type de graphe (à traduire)";
+
     constructor() {
-        super();
         this._observable = new Observable();
     }
 
diff --git a/src/app/components/results-graph/results-graph.component.html b/src/app/components/results-graph/results-graph.component.html
index 73ad69b83bac77af7b831eb1196232bc2248ad29..9f0dc91e235577fd23ee2138e13f465f8b40409c 100644
--- a/src/app/components/results-graph/results-graph.component.html
+++ b/src/app/components/results-graph/results-graph.component.html
@@ -1,12 +1,4 @@
-<div class="row">
-    <div class="col-12">
-        <chart [type]="graph_type" [data]="graph_data" [options]="graph_options">
-        </chart>
-    </div>
-</div>
+<chart [type]="graph_type" [data]="graph_data" [options]="graph_options">
+</chart>
 
-<div class="row">
-    <div class="col-4 mx-auto">
-        <graph-type></graph-type>
-    </div>
-</div>
\ No newline at end of file
+<graph-type></graph-type>
diff --git a/src/app/components/select-field-line/select-field-line.component.html b/src/app/components/select-field-line/select-field-line.component.html
deleted file mode 100644
index c298402bd3203e34793e85c773167d2c1dc9aa19..0000000000000000000000000000000000000000
--- a/src/app/components/select-field-line/select-field-line.component.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<div class="row">
-    <!-- titre -->
-    <div class="col-12 col-sm-3">
-        {{ label }}
-    </div>
-
-    <!-- liste déroulante -->
-    <div class="btn-group col-12 col-sm-9" dropdown (click)="onSelect($event)">
-        <button dropdownToggle class="btn btn-primary dropdown-toggle waves-light my-1" type="button" mdbRippleRadius>
-            {{ currentLabel }}
-        </button>
-        <div class="dropdown-menu">
-            <a class="dropdown-item" *ngFor="let e of entries" [value]=e>{{ entryLabel(e) }}</a>
-        </div>
-    </div>
-</div>
\ No newline at end of file
diff --git a/src/app/components/select-field-line/select-field-line.component.scss b/src/app/components/select-field-line/select-field-line.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..f1fe15c857bdfac181624823629e6c0c4034ce4b
--- /dev/null
+++ b/src/app/components/select-field-line/select-field-line.component.scss
@@ -0,0 +1,14 @@
+mat-form-field {
+    width: 100%;
+
+    mat-select {
+
+        ::ng-deep .mat-select-value {
+            > span {
+                > span {
+                    line-height: 1.3em;
+                }
+            }
+        }
+    }
+}
diff --git a/src/app/components/select-field-line/select-field-line.component.ts b/src/app/components/select-field-line/select-field-line.component.ts
index 59f111c2364ca042154a310ad9d4809bbd74271d..a7840110a5aec8492dcacf86f4fd16775fc2c8b0 100644
--- a/src/app/components/select-field-line/select-field-line.component.ts
+++ b/src/app/components/select-field-line/select-field-line.component.ts
@@ -2,13 +2,16 @@ import { Component, Input } from "@angular/core";
 
 import { SelectField } from "../../formulaire/select-field";
 import { SelectEntry } from "../../formulaire/select-entry";
-import { GenericSelectComponent } from "../generic-select/generic-select.component";
 
 @Component({
     selector: "select-field-line",
-    templateUrl: "./select-field-line.component.html"
+    // templateUrl: "./select-field-line.component.html",
+    templateUrl: "../generic-select/generic-select.component.html",
+    styleUrls: [
+        "./select-field-line.component.scss"
+    ]
 })
-export class SelectFieldLineComponent extends GenericSelectComponent<SelectEntry> {
+export class SelectFieldLineComponent {
     @Input()
     private _select: SelectField;
 
diff --git a/src/app/directives/flex-xxs.directive.ts b/src/app/directives/flex-xxs.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e4df90248cfa5b2c76f0b77b7d804d7e10fd217e
--- /dev/null
+++ b/src/app/directives/flex-xxs.directive.ts
@@ -0,0 +1,65 @@
+import { Directive } from "@angular/core";
+import { BREAKPOINT, ShowHideDirective, FlexDirective } from "@angular/flex-layout";
+
+const XXS_BREAKPOINTS = [
+    {
+        alias: "xxs",
+        mediaQuery: "screen and (max-width: 479px)",
+        overlapping: false
+    },
+    {
+        alias: "xs", // redéfinition
+        mediaQuery: "screen and (min-width: 480px) screen and (max-width: 599px)",
+        overlapping: false
+    },
+    {
+        alias: "gt-xxs",
+        mediaQuery: "screen and (min-width: 480px)",
+        overlapping: false
+    },
+    {
+        alias: "lt-xs",
+        mediaQuery: "screen and (max-width: 479px)",
+        overlapping: false
+    }
+];
+
+export const CustomBreakPointsProvider = {
+    provide: BREAKPOINT,
+    useValue: XXS_BREAKPOINTS,
+    multi: true
+};
+
+const inputsXxs = [ "fxHide.xxs" ];
+const inputsGtXxs = [ "fxHide.gt-xxs" ];
+const inputsLtXs = [ "fxHide.lt-xs" ];
+
+@Directive({
+    // tslint:disable-next-line:directive-selector
+    selector: `[fxHide.xxs]`,
+    // tslint:disable-next-line:use-input-property-decorator
+    inputs: inputsXxs
+})
+export class FlexXxsShowHideDirective extends ShowHideDirective {
+    protected inputs = inputsXxs;
+}
+
+@Directive({
+    // tslint:disable-next-line:directive-selector
+    selector: `[fxHide.gt-xxs]`,
+    // tslint:disable-next-line:use-input-property-decorator
+    inputs: inputsGtXxs
+})
+export class FlexGtXxsShowHideDirective extends ShowHideDirective {
+    protected inputs = inputsGtXxs;
+}
+
+@Directive({
+    // tslint:disable-next-line:directive-selector
+    selector: `[fxHide.lt-xs]`,
+    // tslint:disable-next-line:use-input-property-decorator
+    inputs: inputsLtXs
+})
+export class FlexLtXsShowHideDirective extends ShowHideDirective {
+    protected inputs = inputsLtXs;
+}
diff --git a/src/app/formulaire/definition/concrete/form-regime-uniforme.ts b/src/app/formulaire/definition/concrete/form-regime-uniforme.ts
index b7549d6764dfdb11b89f4b6e65f90b4871981aec..63ed5d5a8249cba95173d3ce91ed355774f7accf 100644
--- a/src/app/formulaire/definition/concrete/form-regime-uniforme.ts
+++ b/src/app/formulaire/definition/concrete/form-regime-uniforme.ts
@@ -1,5 +1,5 @@
 import { FormDefFixedVar } from "../form-def-fixedvar";
-import { CalculatorType, ComputeNodeType, IObservable, Observer } from "jalhyd";
+import { IObservable, Observer } from "jalhyd";
 import { FormResultFixedVar } from "../form-result-fixedvar";
 import { FormulaireDefinition } from "../form-definition";
 import { FormDefSection } from "../form-def-section";
diff --git a/src/app/formulaire/definition/form-def-fixedvar.ts b/src/app/formulaire/definition/form-def-fixedvar.ts
index 9e72537aad47170faa27b75b90e5a6df09bd57d5..9cd7bb4d1b13ecda0c573612e80d9220332778de 100644
--- a/src/app/formulaire/definition/form-def-fixedvar.ts
+++ b/src/app/formulaire/definition/form-def-fixedvar.ts
@@ -143,6 +143,7 @@ export class FormDefFixedVar {
 
             for (const p of this._formBase.allFormElements) {
                 if (p instanceof NgParameter) {
+                    // change all radio button groups except the one that sent the event
                     if (p.radioConfig === ParamRadioConfig.CAL && p.radioState === ParamRadioConfig.FIX && p !== sourceParam) {
                         newCal = p;
                         break;
@@ -152,7 +153,8 @@ export class FormDefFixedVar {
                     break;
                 }
             }
-
+            // if the current calculated parameter was set to another mode, set a new param
+            // to calculated mode (there must always be at least one)
             if (newCal) {
                 newCal.valueMode = ParamValueMode.CALCUL;
             }
@@ -173,9 +175,6 @@ export class FormDefFixedVar {
     public onRadioClick(info: any) {
         const param: NgParameter = info.param; // paramètre source de l'événement radio
         const old: ParamValueMode = info.oldValueMode; // ancien état (radio)
-
-        // this.logParams();
         this.resetRadiosAndResults(param, old);
-        // this.logParams();
     }
 }
diff --git a/src/app/formulaire/ngparam.ts b/src/app/formulaire/ngparam.ts
index 50125790a4fd68293ae9ae70ebc2d952eb698f0b..ad7caff505426de3180a291709579acf25df845b 100644
--- a/src/app/formulaire/ngparam.ts
+++ b/src/app/formulaire/ngparam.ts
@@ -54,7 +54,7 @@ export class NgParameter extends InputField implements Observer {
         this._confId = id;
     }
 
-    public get _paramValues() { // @TODO remettre en private ! (debug)
+    private get _paramValues() {
         return this._paramDef.paramValues;
     }
 
@@ -148,6 +148,17 @@ export class NgParameter extends InputField implements Observer {
         return this._paramDef.isValid;
     }
 
+    public get title(): string {
+        let t = "";
+        if (this.label !== undefined) {
+            t = this.label;
+        }
+        if (this.unit !== undefined && this.unit !== "") {
+            t = t + " (" + this.unit + ")";
+        }
+        return t;
+    }
+
     public get valuesIterator(): INumberIterator {
         return this._paramDef.valuesIterator;
     }
diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json
index 3ba6ffc7113404979bf15a9cbe2ca07f3ce56f68..6f454dc41fef87ba73244198a371354a064b02ce 100644
--- a/src/locale/messages.en.json
+++ b/src/locale/messages.en.json
@@ -31,6 +31,7 @@
     "ERROR_SECTION_NON_CONVERGENCE_NEWTON_HTOR": "Non convergence of the calculation of the corresponding height (Newton's method) for the calculation of the supercritical depth",
     "ERROR_SECTION_PENTE_NEG_NULLE_HNORMALE_INF": "The slope is negative or zero, the normal depth is infinite",
     "ERROR_STRUCTURE_Q_TROP_ELEVE": "The flow passing through the other devices is too high: the requested parameter is not calculable.",
+    "INFO_CALCULATOR_CALC_NAME": "Calculator name",
     "INFO_CALCULATOR_CALCULER": "Compute",
     "INFO_CALCULATOR_PARAMFIXES": "Fixed parameters",
     "INFO_CALCULATOR_VALEURS": "Values",
@@ -123,6 +124,7 @@
     "INFO_OPTION_NO": "No",
     "INFO_OPTION_YES": "Yes",
     "INFO_OPTION_CANCEL": "Cancel",
+    "INFO_OPTION_CLOSE": "Close",
     "INFO_OPTION_LOAD": "Load",
     "INFO_OPTION_SAVE": "Save",
     "INFO_OPTION_ALL": "All",
@@ -137,8 +139,8 @@
     "INFO_PARAMFIELD_PARAMFIXE": "Fixed",
     "INFO_PARAMFIELD_PARAMLIE": "Link",
     "INFO_PARAMFIELD_PARAMVARIER": "Vary",
-    "INFO_PARAMFIELD_PASVARIATION": "with a variation step of:",
-    "INFO_PARAMFIELD_VALEURMAXI": "to maximum value",
+    "INFO_PARAMFIELD_PASVARIATION": "With a variation step of",
+    "INFO_PARAMFIELD_VALEURMAXI": "To maximum value",
     "INFO_PARAMFIELD_VALEURMINI": "From minimum value",
     "INFO_REGIMEUNIFORME_TITRE": "Uniform flow calculation",
     "INFO_REMOUSRESULTS_ABSCISSE": "Abscissa (m)",
diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json
index e5ad681f1a67e4384abbf1346d3c0801f13b1f15..490f83bec45c5d2ae0473c3a22b63ee380b41860 100644
--- a/src/locale/messages.fr.json
+++ b/src/locale/messages.fr.json
@@ -31,6 +31,7 @@
     "ERROR_SECTION_NON_CONVERGENCE_NEWTON_HTOR": "Non convergence du calcul de la hauteur correspondante (Méthode de Newton) pour le calcul de la hauteur torrentielle",
     "ERROR_SECTION_PENTE_NEG_NULLE_HNORMALE_INF": "La pente est négative ou nulle, la hauteur normale est infinie",
     "ERROR_STRUCTURE_Q_TROP_ELEVE": "Le débit passant par les autres ouvrages est trop élevé: le paramètre demandé n'est pas calculable.",
+    "INFO_CALCULATOR_CALC_NAME": "Nom du module de calcul",
     "INFO_CALCULATOR_CALCULER": "Calculer",
     "INFO_CALCULATOR_PARAMFIXES": "Paramètres fixés",
     "INFO_CALCULATOR_VALEURS": "Valeurs",
@@ -123,6 +124,7 @@
     "INFO_OPTION_NO": "Non",
     "INFO_OPTION_YES": "Oui",
     "INFO_OPTION_CANCEL": "Annuler",
+    "INFO_OPTION_CLOSE": "Fermer",
     "INFO_OPTION_LOAD": "Charger",
     "INFO_OPTION_SAVE": "Enregistrer",
     "INFO_OPTION_ALL": "Tous",
@@ -137,8 +139,8 @@
     "INFO_PARAMFIELD_PARAMFIXE": "fixé",
     "INFO_PARAMFIELD_PARAMLIE": "lié",
     "INFO_PARAMFIELD_PARAMVARIER": "varier",
-    "INFO_PARAMFIELD_PASVARIATION": "avec un pas de&nbsp;:",
-    "INFO_PARAMFIELD_VALEURMAXI": "à la valeur maximum",
+    "INFO_PARAMFIELD_PASVARIATION": "Avec un pas de",
+    "INFO_PARAMFIELD_VALEURMAXI": "À la valeur maximum",
     "INFO_PARAMFIELD_VALEURMINI": "De la valeur minimum",
     "INFO_REGIMEUNIFORME_TITRE": "Régime uniforme",
     "INFO_REMOUSRESULTS_ABSCISSE": "Abscisse (m)",
diff --git a/src/styles.scss b/src/styles.scss
index b78e187721fd188a7d3ee9034a13a361bf323a73..c7f1c492cd38e5457a1c5bf66b4dcb18884d8f8d 100644
--- a/src/styles.scss
+++ b/src/styles.scss
@@ -33,3 +33,13 @@ h1 {
     font-weight: 300;
     line-height: 1.2;
 }
+
+// debug
+field-set {
+    margin-bottom: 2em;
+}
+
+// hide elements having "hidden" attribute even if they have explicit "display:" property
+[hidden] {
+    display: none !important;
+}
diff --git a/src/theme.scss b/src/theme.scss
index fddf50617aa7e286ddee09ffb366e4d936cd0129..3ff27ba26eb9f75fb36ca08593d3c606d7e87e66 100644
--- a/src/theme.scss
+++ b/src/theme.scss
@@ -182,6 +182,7 @@ $primary: map-get($nghyd-theme, primary);
 $accent: map-get($nghyd-theme, accent);
 $warn: map-get($nghyd-theme, warn);
 
+// convenience classes (functions mat-* cannot be used outside of this file)
 .color-primary {
     color: mat-color($primary);
 }
@@ -194,9 +195,24 @@ $warn: map-get($nghyd-theme, warn);
 .bg-accent {
     background-color: mat-color($accent);
 }
+.bg-accent-light {
+    background-color: mat-color($accent, 300);
+}
 .color-warn {
     color: mat-color($warn);
 }
 .bg-warn {
     background-color: mat-color($warn);
 }
+
+// make toggle buttons more visible
+/* .mat-button-toggle {
+    background-color: mat-color($primary);
+    color: mat-color($primary, default-contrast);
+}
+ */
+
+.mat-button-toggle-checked {
+    background-color: mat-color($accent);
+    color: mat-color($accent, default-contrast) !important;
+}
diff --git a/tsconfig.json b/tsconfig.json
index cb174fd8a71047dfd9d30bce1acb17757e54f155..f6d913f28b3fb0497654c6b9cc9f5c726ac0ad09 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -20,7 +20,6 @@
     "baseUrl": "./"
   },
   "include": [
-    "node_modules/angular-bootstrap-md/**/*.ts",
     "src/**/*.ts"
   ]
 }
\ No newline at end of file