diff --git a/.gitignore b/.gitignore
index f9db6fa19727f326db8c70b4dad8791827b60999..371089b341039668cbe0bcb651773ce321703d53 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,8 @@
 # See http://help.github.com/ignore-files/ for more about ignoring files.
 
+# config
+/src/app/config.json
+
 # compiled output
 /dist
 /tmp
diff --git a/src/app/app.component.html b/src/app/app.component.html
index 8a1b870615f7333a902854337b434d895d530801..2df5abc73ff0aaf5faf27a31081c0f93bb6aa693 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -103,13 +103,7 @@
     </mat-sidenav>
 
     <mat-sidenav-content class="sidenav-content" fxFlexFill>
-      <div class="container-fluid">
-        <div class="row">
-          <div class="col-12">
-            <router-outlet (activate)="onRouterOutletActivated($event)"></router-outlet>
-          </div>
-        </div>
-      </div>
+      <router-outlet (activate)="onRouterOutletActivated($event)"></router-outlet>
     </mat-sidenav-content>
 
   </mat-sidenav-container>
diff --git a/src/app/components/calculator-list/calculator-list.component.css b/src/app/components/calculator-list/calculator-list.component.css
deleted file mode 100644
index 107f363974953c98e69a1bfc896224fb9b87e84a..0000000000000000000000000000000000000000
--- a/src/app/components/calculator-list/calculator-list.component.css
+++ /dev/null
@@ -1,16 +0,0 @@
-.full-page {
-    width: 100%;
-    height: 100%;
-    min-height: 100%;
-    background: red;
-    display: block;
-}
-
-.full-height { 
-    min-height: 100%;
-    height: 100%;
-}
-
-button:focus {
-    outline: 0;
-}
diff --git a/src/app/components/calculator-list/calculator-list.component.html b/src/app/components/calculator-list/calculator-list.component.html
index 7f8497ad9e951393574bbfd5ca62125eb75b7bef..e8b083588ce1145dfe2ffc0e37f736a02a5908ac 100644
--- a/src/app/components/calculator-list/calculator-list.component.html
+++ b/src/app/components/calculator-list/calculator-list.component.html
@@ -1,10 +1,26 @@
-<div class="container-fluid">
-    <div class="row">
-        <div class="offset-1 col-10 mx-auto">
-            <ul *ngFor="let l of items" class="list-group">
-                <!-- on utilise [innerHTML] pour que les codes HTML comme &nbsp; soient interprétés correctement -->
-                <button class="list-group-item" (click)="create(l.type)" [innerHTML]="l.label"></button>
-            </ul>
-        </div>
-    </div>
-</div>
\ No newline at end of file
+<div class="container" fxLayout="row wrap" fxLayoutAlign="space-evenly stretch">
+
+    <mat-card *ngFor="let theme of items" class="compute-nodes-theme">
+
+        <mat-card-header>
+            <!-- <img mat-card-avatar src="https://s3.amazonaws.com/pix.iemoji.com/images/emoji/apple/ios-12/256/water-wave.png"> -->
+            <mat-card-title>{{ theme.title }}</mat-card-title>
+            <mat-card-subtitle>{{ theme.description }}</mat-card-subtitle>
+        </mat-card-header>
+
+        <!-- <img mat-card-image src="http://maurice.rolland.pagesperso-orange.fr/barduser01.jpg"> -->
+
+        <!-- <mat-card-content>
+            <p>{{ theme.description }}</p>
+        </mat-card-content> -->
+
+        <mat-card-actions>
+            <div class="container" fxLayout="column" fxLayoutAlign="left" fxLayoutGap="10px">
+                <button mat-raised-button color="accent" *ngFor="let calc of theme.calculators" class="theme-calculator"
+                    (click)="create(calc.type)" [innerHTML]="calc.label"></button>
+            </div>
+        </mat-card-actions>
+    
+    </mat-card>
+
+</div>
diff --git a/src/app/components/calculator-list/calculator-list.component.scss b/src/app/components/calculator-list/calculator-list.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..0d8f2afd2ead503f78ebf9c0af85287f0c166dc2
--- /dev/null
+++ b/src/app/components/calculator-list/calculator-list.component.scss
@@ -0,0 +1,13 @@
+mat-card.compute-nodes-theme {
+    width: 300px;
+    margin: 1em;
+
+    button.theme-calculator {
+        margin-left: 0;
+        margin-right: 0;
+    }
+
+    .mat-card-actions:last-child {
+        margin-bottom: 0; // instead of -8px
+    }
+}
diff --git a/src/app/components/calculator-list/calculator-list.component.ts b/src/app/components/calculator-list/calculator-list.component.ts
index 910568f99c394980c70bc56bcafc3ebfb981664c..2af92fe5f19110d66eb6e3334be391d0722e34cf 100644
--- a/src/app/components/calculator-list/calculator-list.component.ts
+++ b/src/app/components/calculator-list/calculator-list.component.ts
@@ -5,40 +5,74 @@ import { CalculatorType, EnumEx } from "jalhyd";
 
 import { FormulaireDefinition } from "../../formulaire/definition/form-definition";
 import { ServiceFactory } from "../../services/service-factory";
-import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
 import { FormulaireParallelStructure } from "../../formulaire/definition/concrete/form-parallel-structures";
 import { FieldsetContainer } from "../../formulaire/fieldset-container";
 
-class ListElement {
-    private _label: string;
-
-    constructor(private _type: CalculatorType) {
-        this._label = ServiceFactory.instance.formulaireService.getLocalisedTitleFromCalculatorType(_type);
-    }
-
-    public get label() { return this._label; }
-    public get type() { return this._type; }
-}
 
 @Component({
     selector: "list",
     templateUrl: "./calculator-list.component.html",
-    styleUrls: ["./calculator-list.component.css"]
+    styleUrls: ["./calculator-list.component.scss"]
 })
 export class CalculatorListComponent implements OnInit {
-    private _items: ListElement[];
+    private _items: any[];
 
     constructor(private router: Router) {
-        ServiceFactory.instance.internationalisationService.addObserver(this);
+        ServiceFactory.instance.i18nService.addObserver(this);
+        ServiceFactory.instance.applicationSetupService.addObserver(this);
     }
 
-    private updateLocale() {
+    /** triggered on init */
+    private loadCalculatorsThemes() {
         this._items = [];
-        for (const t of EnumEx.getValues(CalculatorType)) {
-            if (t !== CalculatorType.Structure) {
-                this._items.push(new ListElement(t));
+        const unusedCalculators = EnumEx.getValues(CalculatorType);
+        const themes = ServiceFactory.instance.applicationSetupService.themes;
+        if (themes) {
+            // group by themes
+            for (const theme of themes) {
+                // get theme details from config
+                const themeTitleKey = "INFO_THEME_" + theme.name + "_TITRE";
+                const themeDescriptionKey = "INFO_THEME_" + theme.name + "_DESCRIPTION";
+                const item = {
+                    title: ServiceFactory.instance.i18nService.localizeText(themeTitleKey),
+                    description: ServiceFactory.instance.i18nService.localizeText(themeDescriptionKey),
+                    calculators: []
+                };
+                // get calculators for this theme
+                for (const calcType of theme.calculators) {
+                    item.calculators.push({
+                        type: calcType,
+                        label: ServiceFactory.instance.formulaireService.getLocalisedTitleFromCalculatorType(calcType)
+                    });
+                    // mark as used
+                    const index = unusedCalculators.indexOf(calcType);
+                    if (index > -1) {
+                        unusedCalculators.splice(index, 1);
+                    }
+                }
+                this._items.push(item);
             }
         }
+        // extra card for unused calculators
+        if (unusedCalculators.length > 0) {
+            const unusedTheme = {
+                title: "Pas utilisées",
+                description: "Tout ce qu'est pas dans les autres thèmes",
+                calculators: []
+            };
+            for (const t of unusedCalculators) {
+                if (t !== CalculatorType.Structure) {
+                    unusedTheme.calculators.push({
+                        type: t,
+                        label: ServiceFactory.instance.formulaireService.getLocalisedTitleFromCalculatorType(t)
+                    });
+                }
+            }
+            if (unusedTheme.calculators.length > 0) {
+                this._items.push(unusedTheme);
+            } // else the only remaining calculator was "Structure", the one we don't want
+        }
     }
 
     private create(t: CalculatorType) {
@@ -66,14 +100,12 @@ export class CalculatorListComponent implements OnInit {
     // interface Observer
 
     update(sender: any, data: any): void {
-        if (sender instanceof InternationalisationService) {
-            this.updateLocale();
+        if (sender instanceof I18nService) {
+            this.loadCalculatorsThemes();
         }
     }
 
-    // OnInit
-
     ngOnInit() {
-        this.updateLocale();
+        this.loadCalculatorsThemes();
     }
 }
diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json
index 1fad871b858d0514cc51d4b4e5ab500cf4d30cfb..90a9ecefab9af83f5d3e3e8df60c4ff43174ebe3 100644
--- a/src/locale/messages.en.json
+++ b/src/locale/messages.en.json
@@ -125,6 +125,16 @@
     "INFO_SETUP_PRECISION_AFFICHAGE": "Display accuracy",
     "INFO_SETUP_PRECISION_CALCUL": "Computation accuracy",
     "INFO_SETUP_TITLE": "Application setup",
+    "INFO_THEME_PASSE_A_BASSIN_TITRE": "Basin pass",
+    "INFO_THEME_PASSE_A_BASSIN_DESCRIPTION": "A great pass with a great basin",
+    "INFO_THEME_PASSE_NATURELLE_TITRE": "Natural pass",
+    "INFO_THEME_PASSE_NATURELLE_DESCRIPTION": "A great and very natural pass",
+    "INFO_THEME_HYDRAULIQUE_A_SURFACE_LIBRE_TITRE": "Hydraulics on free surface",
+    "INFO_THEME_HYDRAULIQUE_A_SURFACE_LIBRE_DESCRIPTION": "Hydraulics on free surface is great!",
+    "INFO_THEME_HYDRAULIQUE_EN_CHARGE_TITRE": "Hydraulics on charge",
+    "INFO_THEME_HYDRAULIQUE_EN_CHARGE_DESCRIPTION": "Hydraulics on charge is great!",
+    "INFO_THEME_LOIS_D_OUVRAGES_TITRE": "Parallel structures",
+    "INFO_THEME_LOIS_D_OUVRAGES_DESCRIPTION": "Parallel structures are great!",
     "WARNING_REMOUS_ARRET_CRITIQUE": "Calculation stopped: critical elevation reached at abscissa %x%",
     "WARNING_STRUCTUREKIVI_HP_TROP_ELEVE": "h/p must not be greater than 2.5. h/p is forced to 2.5",
     "WARNING_STRUCTUREKIVI_PELLE_TROP_FAIBLE": "Threshold height should be greater than 0.1 m. Beta coefficient is forced to 0"
diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json
index 0772944e520fd37849da255b7e8dddac74f829f9..3190c9fa762b9c6ddbbe224ecefd8d8419ebcf93 100644
--- a/src/locale/messages.fr.json
+++ b/src/locale/messages.fr.json
@@ -140,6 +140,16 @@
     "INFO_SETUP_PRECISION_AFFICHAGE": "Précision d'affichage",
     "INFO_SETUP_PRECISION_CALCUL": "Précision de calcul",
     "INFO_SETUP_TITLE": "Paramètres de l'application",
+    "INFO_THEME_PASSE_A_BASSIN_TITRE": "Passe à bassin",
+    "INFO_THEME_PASSE_A_BASSIN_DESCRIPTION": "Une super passe avec un super bassin",
+    "INFO_THEME_PASSE_NATURELLE_TITRE": "Passe naturelle",
+    "INFO_THEME_PASSE_NATURELLE_DESCRIPTION": "Une super passe super naturelle",
+    "INFO_THEME_HYDRAULIQUE_A_SURFACE_LIBRE_TITRE": "Hydraulique à surface libre",
+    "INFO_THEME_HYDRAULIQUE_A_SURFACE_LIBRE_DESCRIPTION": "L'hydraulique à surface libre c'est super !",
+    "INFO_THEME_HYDRAULIQUE_EN_CHARGE_TITRE": "Hydraulique en charge",
+    "INFO_THEME_HYDRAULIQUE_EN_CHARGE_DESCRIPTION": "L'hydraulique en charge c'est super !",
+    "INFO_THEME_LOIS_D_OUVRAGES_TITRE": "Lois d'ouvrages",
+    "INFO_THEME_LOIS_D_OUVRAGES_DESCRIPTION": "Les lois d'ouvrages c'est super !",
     "WARNING_REMOUS_ARRET_CRITIQUE": "Arrêt du calcul&nbsp;: hauteur critique atteinte à l'abscisse %x%",
     "WARNING_STRUCTUREKIVI_HP_TROP_ELEVE": "h/p ne doit pas être supérieur à 2,5. h/p est forcé à 2,5",
     "WARNING_STRUCTUREKIVI_PELLE_TROP_FAIBLE": "La pelle du seuil doit mesurer au moins 0,1 m. Le coefficient béta est forcé à 0"