From 9d1023935ebec47454b357fd6404257145909616 Mon Sep 17 00:00:00 2001
From: FORESTIER Fabien <fabien.forestier@soprasteria.com>
Date: Fri, 23 Mar 2018 13:38:59 +0100
Subject: [PATCH] Add bootstrap-grid, create core module, dive the app in three
 main component header, main, footer, add base of theming

---
 webapp/.angular-cli.json                      |   3 +-
 webapp/package-lock.json                      |   6 +
 webapp/package.json                           |   1 +
 webapp/src/app/app-routing.module.ts          |  12 +
 webapp/src/app/app.component.html             |  20 +-
 webapp/src/app/app.module.ts                  |   7 +-
 .../components/footer/footer.component.html   |   3 +
 .../components/footer/footer.component.scss   |   0
 .../footer/footer.component.spec.ts           |  25 +
 .../components/footer/footer.component.ts     |  15 +
 .../components/header/header.component.html   |   3 +
 .../components/header/header.component.scss   |   0
 .../header/header.component.spec.ts           |  25 +
 .../components/header/header.component.ts     |  15 +
 .../core/components/main/main.component.html  |   9 +
 .../core/components/main/main.component.scss  |   0
 .../components/main/main.component.spec.ts    |  25 +
 .../core/components/main/main.component.ts    |  15 +
 webapp/src/app/core/core-routing.module.ts    |  13 +
 webapp/src/app/core/core.module.ts            |  18 +
 .../src/assets/img/android-chrome-192x192.png | Bin 0 -> 5193 bytes
 .../src/assets/img/android-chrome-512x512.png | Bin 0 -> 15280 bytes
 webapp/src/assets/img/apple-touch-icon.png    | Bin 0 -> 4823 bytes
 webapp/src/assets/img/favicon-16x16.png       | Bin 0 -> 727 bytes
 webapp/src/assets/img/favicon-32x32.png       | Bin 0 -> 1354 bytes
 webapp/src/assets/img/favicon.ico             | Bin 0 -> 15086 bytes
 webapp/src/assets/img/logo.svg                |   5 +
 webapp/src/scss/_functions.scss               |  86 ++
 webapp/src/scss/_grid.scss                    |  52 +
 webapp/src/scss/_variables.scss               | 894 ++++++++++++++++++
 webapp/src/scss/bootstrap-grid.scss           |  32 +
 webapp/src/scss/mixins/_breakpoints.scss      | 123 +++
 webapp/src/scss/mixins/_grid-framework.scss   |  67 ++
 webapp/src/scss/mixins/_grid.scss             |  52 +
 webapp/src/scss/utilities/_display.scss       |  38 +
 webapp/src/scss/utilities/_flex.scss          |  46 +
 webapp/src/styles.scss                        |  18 +
 webapp/src/theme.scss                         |  17 +
 webapp/src/theming/_all-theme.scss            |  29 +
 webapp/src/theming/_theming.scss              |  11 +
 .../theming/grandlyon/config/_palette.scss    |  89 ++
 .../theming/grandlyon/config/_variables.scss  |   9 +
 webapp/src/theming/grandlyon/grandlyon.scss   |  10 +
 43 files changed, 1771 insertions(+), 22 deletions(-)
 create mode 100644 webapp/src/app/app-routing.module.ts
 create mode 100644 webapp/src/app/core/components/footer/footer.component.html
 create mode 100644 webapp/src/app/core/components/footer/footer.component.scss
 create mode 100644 webapp/src/app/core/components/footer/footer.component.spec.ts
 create mode 100644 webapp/src/app/core/components/footer/footer.component.ts
 create mode 100644 webapp/src/app/core/components/header/header.component.html
 create mode 100644 webapp/src/app/core/components/header/header.component.scss
 create mode 100644 webapp/src/app/core/components/header/header.component.spec.ts
 create mode 100644 webapp/src/app/core/components/header/header.component.ts
 create mode 100644 webapp/src/app/core/components/main/main.component.html
 create mode 100644 webapp/src/app/core/components/main/main.component.scss
 create mode 100644 webapp/src/app/core/components/main/main.component.spec.ts
 create mode 100644 webapp/src/app/core/components/main/main.component.ts
 create mode 100644 webapp/src/app/core/core-routing.module.ts
 create mode 100644 webapp/src/app/core/core.module.ts
 create mode 100644 webapp/src/assets/img/android-chrome-192x192.png
 create mode 100644 webapp/src/assets/img/android-chrome-512x512.png
 create mode 100644 webapp/src/assets/img/apple-touch-icon.png
 create mode 100644 webapp/src/assets/img/favicon-16x16.png
 create mode 100644 webapp/src/assets/img/favicon-32x32.png
 create mode 100644 webapp/src/assets/img/favicon.ico
 create mode 100644 webapp/src/assets/img/logo.svg
 create mode 100644 webapp/src/scss/_functions.scss
 create mode 100644 webapp/src/scss/_grid.scss
 create mode 100644 webapp/src/scss/_variables.scss
 create mode 100644 webapp/src/scss/bootstrap-grid.scss
 create mode 100644 webapp/src/scss/mixins/_breakpoints.scss
 create mode 100644 webapp/src/scss/mixins/_grid-framework.scss
 create mode 100644 webapp/src/scss/mixins/_grid.scss
 create mode 100644 webapp/src/scss/utilities/_display.scss
 create mode 100644 webapp/src/scss/utilities/_flex.scss
 create mode 100644 webapp/src/theme.scss
 create mode 100644 webapp/src/theming/_all-theme.scss
 create mode 100644 webapp/src/theming/_theming.scss
 create mode 100644 webapp/src/theming/grandlyon/config/_palette.scss
 create mode 100644 webapp/src/theming/grandlyon/config/_variables.scss
 create mode 100644 webapp/src/theming/grandlyon/grandlyon.scss

diff --git a/webapp/.angular-cli.json b/webapp/.angular-cli.json
index 862071be..8310aa96 100644
--- a/webapp/.angular-cli.json
+++ b/webapp/.angular-cli.json
@@ -19,7 +19,8 @@
       "testTsconfig": "tsconfig.spec.json",
       "prefix": "app",
       "styles": [
-        "styles.scss"
+        "styles.scss",
+        "theme.scss"
       ],
       "scripts": [],
       "environmentSource": "environments/environment.ts",
diff --git a/webapp/package-lock.json b/webapp/package-lock.json
index 9ba90098..090f6bca 100644
--- a/webapp/package-lock.json
+++ b/webapp/package-lock.json
@@ -9167,6 +9167,12 @@
         "pify": "3.0.0"
       }
     },
+    "sass-recursive-map-merge": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/sass-recursive-map-merge/-/sass-recursive-map-merge-1.0.1.tgz",
+      "integrity": "sha512-OuDTGVGx2o2sPeaSgGob5s2Qf6LxoMU4LG7n6vCzNgfXyBz/y8tKzcEYdmvgyhjvGQVcGA1g2UJnP7WMmahuVg==",
+      "dev": true
+    },
     "saucelabs": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.3.0.tgz",
diff --git a/webapp/package.json b/webapp/package.json
index 20789ef7..01a56507 100644
--- a/webapp/package.json
+++ b/webapp/package.json
@@ -45,6 +45,7 @@
     "protractor": "~5.1.2",
     "ts-node": "~4.1.0",
     "tslint": "~5.9.1",
+    "sass-recursive-map-merge": "^1.0.1",
     "typescript": "~2.5.3"
   }
 }
diff --git a/webapp/src/app/app-routing.module.ts b/webapp/src/app/app-routing.module.ts
new file mode 100644
index 00000000..5c38405b
--- /dev/null
+++ b/webapp/src/app/app-routing.module.ts
@@ -0,0 +1,12 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+const routes: Routes = [
+  { path: '', redirectTo: 'app', pathMatch: 'full' }
+];
+
+@NgModule({
+  imports: [RouterModule.forRoot(routes)],
+  exports: [RouterModule]
+})
+export class AppRoutingModule { }
diff --git a/webapp/src/app/app.component.html b/webapp/src/app/app.component.html
index fa2706a4..6a164d69 100644
--- a/webapp/src/app/app.component.html
+++ b/webapp/src/app/app.component.html
@@ -1,20 +1,2 @@
-<!--The content below is only a placeholder and can be replaced.-->
-<div style="text-align:center">
-  <h1>
-    Welcome to {{ title }}!
-  </h1>
-  <img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
-</div>
-<h2>Here are some links to help you start: </h2>
-<ul>
-  <li>
-    <h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial">Tour of Heroes</a></h2>
-  </li>
-  <li>
-    <h2><a target="_blank" rel="noopener" href="https://github.com/angular/angular-cli/wiki">CLI Documentation</a></h2>
-  </li>
-  <li>
-    <h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2>
-  </li>
-</ul>
+<router-outlet></router-outlet>
 
diff --git a/webapp/src/app/app.module.ts b/webapp/src/app/app.module.ts
index f4b848b8..2d9e72fb 100644
--- a/webapp/src/app/app.module.ts
+++ b/webapp/src/app/app.module.ts
@@ -4,8 +4,9 @@ import { NgModule } from '@angular/core';
 
 import { AppMaterialModule } from './app.material.module';
 
-
 import { AppComponent } from './app.component';
+import { AppRoutingModule } from './app-routing.module';
+import { CoreModule } from './core/core.module';
 
 
 @NgModule({
@@ -15,7 +16,9 @@ import { AppComponent } from './app.component';
   imports: [
     BrowserModule,
     BrowserAnimationsModule,
-    AppMaterialModule
+    AppMaterialModule,
+    CoreModule,
+    AppRoutingModule
   ],
   providers: [],
   bootstrap: [AppComponent]
diff --git a/webapp/src/app/core/components/footer/footer.component.html b/webapp/src/app/core/components/footer/footer.component.html
new file mode 100644
index 00000000..6800e0eb
--- /dev/null
+++ b/webapp/src/app/core/components/footer/footer.component.html
@@ -0,0 +1,3 @@
+<p>
+  footer works!
+</p>
diff --git a/webapp/src/app/core/components/footer/footer.component.scss b/webapp/src/app/core/components/footer/footer.component.scss
new file mode 100644
index 00000000..e69de29b
diff --git a/webapp/src/app/core/components/footer/footer.component.spec.ts b/webapp/src/app/core/components/footer/footer.component.spec.ts
new file mode 100644
index 00000000..2ca6c454
--- /dev/null
+++ b/webapp/src/app/core/components/footer/footer.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { FooterComponent } from './footer.component';
+
+describe('FooterComponent', () => {
+  let component: FooterComponent;
+  let fixture: ComponentFixture<FooterComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ FooterComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(FooterComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/webapp/src/app/core/components/footer/footer.component.ts b/webapp/src/app/core/components/footer/footer.component.ts
new file mode 100644
index 00000000..da17d824
--- /dev/null
+++ b/webapp/src/app/core/components/footer/footer.component.ts
@@ -0,0 +1,15 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+  selector: 'app-footer',
+  templateUrl: './footer.component.html',
+  styleUrls: ['./footer.component.scss']
+})
+export class FooterComponent implements OnInit {
+
+  constructor() { }
+
+  ngOnInit() {
+  }
+
+}
diff --git a/webapp/src/app/core/components/header/header.component.html b/webapp/src/app/core/components/header/header.component.html
new file mode 100644
index 00000000..c3dba658
--- /dev/null
+++ b/webapp/src/app/core/components/header/header.component.html
@@ -0,0 +1,3 @@
+<mat-toolbar color="primary">
+  <span>OpenData</span>
+</mat-toolbar>
\ No newline at end of file
diff --git a/webapp/src/app/core/components/header/header.component.scss b/webapp/src/app/core/components/header/header.component.scss
new file mode 100644
index 00000000..e69de29b
diff --git a/webapp/src/app/core/components/header/header.component.spec.ts b/webapp/src/app/core/components/header/header.component.spec.ts
new file mode 100644
index 00000000..2d0479d7
--- /dev/null
+++ b/webapp/src/app/core/components/header/header.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { HeaderComponent } from './header.component';
+
+describe('HeaderComponent', () => {
+  let component: HeaderComponent;
+  let fixture: ComponentFixture<HeaderComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ HeaderComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(HeaderComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/webapp/src/app/core/components/header/header.component.ts b/webapp/src/app/core/components/header/header.component.ts
new file mode 100644
index 00000000..591e148a
--- /dev/null
+++ b/webapp/src/app/core/components/header/header.component.ts
@@ -0,0 +1,15 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+  selector: 'app-header',
+  templateUrl: './header.component.html',
+  styleUrls: ['./header.component.scss']
+})
+export class HeaderComponent implements OnInit {
+
+  constructor() { }
+
+  ngOnInit() {
+  }
+
+}
diff --git a/webapp/src/app/core/components/main/main.component.html b/webapp/src/app/core/components/main/main.component.html
new file mode 100644
index 00000000..bfa5088f
--- /dev/null
+++ b/webapp/src/app/core/components/main/main.component.html
@@ -0,0 +1,9 @@
+<app-header></app-header>
+
+<div class="container-fluid">
+  <p>
+    main works!
+  </p>
+</div>
+
+<app-footer></app-footer>
diff --git a/webapp/src/app/core/components/main/main.component.scss b/webapp/src/app/core/components/main/main.component.scss
new file mode 100644
index 00000000..e69de29b
diff --git a/webapp/src/app/core/components/main/main.component.spec.ts b/webapp/src/app/core/components/main/main.component.spec.ts
new file mode 100644
index 00000000..0878044a
--- /dev/null
+++ b/webapp/src/app/core/components/main/main.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MainComponent } from './main.component';
+
+describe('MainComponent', () => {
+  let component: MainComponent;
+  let fixture: ComponentFixture<MainComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ MainComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(MainComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/webapp/src/app/core/components/main/main.component.ts b/webapp/src/app/core/components/main/main.component.ts
new file mode 100644
index 00000000..8b899ba8
--- /dev/null
+++ b/webapp/src/app/core/components/main/main.component.ts
@@ -0,0 +1,15 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+  selector: 'app-main',
+  templateUrl: './main.component.html',
+  styleUrls: ['./main.component.scss']
+})
+export class MainComponent implements OnInit {
+
+  constructor() { }
+
+  ngOnInit() {
+  }
+
+}
diff --git a/webapp/src/app/core/core-routing.module.ts b/webapp/src/app/core/core-routing.module.ts
new file mode 100644
index 00000000..90928dee
--- /dev/null
+++ b/webapp/src/app/core/core-routing.module.ts
@@ -0,0 +1,13 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+import { MainComponent } from './components/main/main.component';
+
+const routes: Routes = [
+  { path: 'app', component: MainComponent }
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+export class CoreRoutingModule { }
diff --git a/webapp/src/app/core/core.module.ts b/webapp/src/app/core/core.module.ts
new file mode 100644
index 00000000..e7e45744
--- /dev/null
+++ b/webapp/src/app/core/core.module.ts
@@ -0,0 +1,18 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+import { CoreRoutingModule } from './core-routing.module';
+import { HeaderComponent } from './components/header/header.component';
+import { MainComponent } from './components/main/main.component';
+import { FooterComponent } from './components/footer/footer.component';
+import { AppMaterialModule } from '../app.material.module';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    CoreRoutingModule,
+    AppMaterialModule
+  ],
+  declarations: [HeaderComponent, MainComponent, FooterComponent]
+})
+export class CoreModule { }
diff --git a/webapp/src/assets/img/android-chrome-192x192.png b/webapp/src/assets/img/android-chrome-192x192.png
new file mode 100644
index 0000000000000000000000000000000000000000..27c7e19579c49567b1de0625ddf670927b0718e7
GIT binary patch
literal 5193
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

literal 0
HcmV?d00001

diff --git a/webapp/src/assets/img/android-chrome-512x512.png b/webapp/src/assets/img/android-chrome-512x512.png
new file mode 100644
index 0000000000000000000000000000000000000000..70d2e5c65b6f2d949814afaefbf7dfa0521687ea
GIT binary patch
literal 15280
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
SbSeV_1B0ilpUXO@geCy@HxE1j

literal 0
HcmV?d00001

diff --git a/webapp/src/assets/img/apple-touch-icon.png b/webapp/src/assets/img/apple-touch-icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..f6a9f6786a18ae9941594b9731ad679fe919be15
GIT binary patch
literal 4823
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
N!PC{xWt~$(6972o>Q4Xw

literal 0
HcmV?d00001

diff --git a/webapp/src/assets/img/favicon-16x16.png b/webapp/src/assets/img/favicon-16x16.png
new file mode 100644
index 0000000000000000000000000000000000000000..1870e1d32ea23a1106918dc041f86739f2589f61
GIT binary patch
literal 727
zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4rT@h1`S>QU<L*TmUKs7M+SzC{oH>NSs54@
z6p}rHd>I(3)EF2VS{N99F)%PRykKA`HDF+PmB7GYHG_dcykO3*KpO@IhQk3qA+8Jz
z3~yyr{u|h_va!p^D=5n=yp>UTE2F~9#?H#d4q>B_pJi2bxOf=B3RsayG-YpPRNl#`
z{FYTS<l<pqWh1Eaij2w?SrtWYUJgz!E-r2^E^ZD^E>13PPEIZvc?Csz1vMpQRRu*5
zQdCk^R8mn;R8>^^|Np;1seUs91A{|JkRK>W7#J9s7#SJ0<aMPLl~mQt)x3Q|`~pqP
z>>SN)&6QKkic-2aDN71UiAjj5s#&-jXn7dBaR~5=@(YLxiTv-MSkJ(~z?kIi?&9$8
zX3Px+1_t&LPhVH|M@&py)&>E&7aJHD7?M0)977~7CnqdmO-N2pW??EbQ{(gV^Gi!(
z)60{Kv#X0^bW}_f3^Xj{Wb@R_^puRWtYmdnO%+WIHDxR1_0`RljkT?1c2-Un4mM_U
zGe|JpzG2IzZ5y|4Zl8IUi`k^mk|E|AN3-}Zml_5J2GtVRh?11Vl2ohYqSVBaR0bmh
zLknF4BV7Zd5JLkiQ%fro3vB~KD+2@lD(Pqx4Y~O#nQ4`{HN>eee$K$a5MC7$Q4*9`
zu24{vpO%@Es!&o{kgAYbP?F5RP%-E6CmxQ%Fb$1U{-@7)J`G}ER_4}A<`z~K_MR-l
zEUe(tU~)KxS$T7a!s#1VP8>ONMCJ(l=?0GlUV03##05(}IhjsnU|?YIboFyt=akR{
E0L~@5od5s;

literal 0
HcmV?d00001

diff --git a/webapp/src/assets/img/favicon-32x32.png b/webapp/src/assets/img/favicon-32x32.png
new file mode 100644
index 0000000000000000000000000000000000000000..f6297745e4fddbc6d05d33642f5fbd858f1cbef6
GIT binary patch
literal 1354
zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4rT@h2A3sW#~2tGSkfJR9T^xl_H+M9WCckk
zdj$D1FjT2AFf_C<F#KX*U}$*3z))(y!0;-8fx&791A}<MoLzx73=B*%0X`wF3=9l!
zWmK40Se4`zl;suP%BT>DKFFvja&ob<u@giD4g4sh(kvv-OoUOaZ0vYU`y#74S44`D
zkU4lvK$iL}t2#?WiiwbE_?`4#M&-SX%10TMJOL3FLPo)D-zBSDAg8oYR(XM}GOLKF
zn20DJFQ2%mn3RO1sEDYlf})(1G#@XYw4{`@grumjh?1PVsEDYNyaF7lfMZjc0`mX=
z|GVbQoz1|&u(>42FPH%Yq;&<j^>p|pI9X+QmGouUby;N<g*7ET<oI-q0#(d3oK#)e
z;)?v8b>|3~O{~tG&_BoJw9PvE00#>>5iK_s4Z8)R%v%lSiN{@9dRQpb$j(#2-8a!j
zEk#q$YOy*;M2pB`<EZI<KRnFkm4&SYO&LAyU8JYaPIFF5U06`0w33gRiCIj7i%C*U
znxBu2ON5(~pI4Ys#z36STER?ON>{~5U0KG4nS+Iqos)-+)w0k&m4ShQG0EHAW%I4v
z|8)!u4D2PIzOL+#n3%Y%4FYm6HZU+Sa(cQrhFF|VPLN=2X4|l7V{t)Iq4B2=pFSFM
z6>xBIc6W4jdV6?zo<4EvWVfb)hL+~)6{}WeXA~7?e=Jy;vHInUS1+X{q$Fq0m^Cvx
zBFZr0b3xI~8@F!Wj<UY7Ld)99a`%p1JF90@R8{_-@$=VDc7qk{Y^?1qZLRJuR&K7x
zk62kAKdP>xrn-E|vZc#YQ_@n6pWJx%?5X$-QL&XG+*5C?m^y80ct}|2^(#hKubNkw
znwc7Z*t%ugR>Ld{<7pP#jkawoFDWbi{^i?O{}~pUpQfz5yzIQgSE0{mA~!!jGuOQS
z;GfoRG5yG0IX}CmuHcG@2wNMsx$N$&tJ&AqL~XA-e9U+CHU1Kl9wrtBhE1{Fr&pg|
z+Qq=YpjzS@QIe8al4_M)l$uzQ%3x$*XrXIhq-$UlVrXDxYH4L+p>1GjWniFRB^`~T
zAvZrIGp!Q0hB)=b&lwmP!mC0eN`ey06$*;-(=u~X6-p`#QWY`_N|G5ED&{=?#KTb-
zrlE1l|MVHpr$G$N%G`R%+``Jj-jhX`g%w;HOb(|oD{l@_IDO;Fi6du@$Q)rm-Qcmn
aOON4|xM0aAC)25*^y=yA=d#Wzp$Py0a)3zy

literal 0
HcmV?d00001

diff --git a/webapp/src/assets/img/favicon.ico b/webapp/src/assets/img/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..5619a8f1cf3db720725e00d80636e23de406e8de
GIT binary patch
literal 15086
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
bMpie<CO-rqW2~U@Rvh>b4sr0<ECT}oShyQF

literal 0
HcmV?d00001

diff --git a/webapp/src/assets/img/logo.svg b/webapp/src/assets/img/logo.svg
new file mode 100644
index 00000000..2bb541a6
--- /dev/null
+++ b/webapp/src/assets/img/logo.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="185.6" height="37.9" viewBox="0 0 185.6 37.9">
+  <path d="M14.4 21.3C12.8 23 10.5 24 8.1 24c-2.2 0-4.4-.9-5.9-2.4C0 19.5 0 17.4 0 12c0-5.3 0-7.4 2.2-9.6C3.8.9 5.9 0 8.1 0c4.2 0 7.4 2.7 8.1 6.9h-1.8c-.7-3.3-3.1-5.3-6.3-5.3-1.7 0-3.3.6-4.5 1.8C1.9 5.1 1.8 6.9 1.8 12s.1 6.9 1.8 8.6c1.2 1.2 2.8 1.8 4.5 1.8 1.9 0 3.7-.8 4.9-2.3 1-1.3 1.4-2.7 1.4-4.7v-2.1H8.1v-1.6h8.2v3.8c0 2.6-.5 4.3-1.9 5.8zm22.2 2.5l-5.7-10.9h-6.5v10.9h-1.8V.2h9c3.9 0 6.8 2.2 6.8 6.3 0 3.5-2.1 5.8-5.5 6.4l5.7 10.9h-2zm-5.2-22h-7v9.6h7c3 0 5.2-1.5 5.2-4.8s-2.2-4.8-5.2-4.8m27 22l-2.1-5.7h-11l-2.1 5.7h-1.9L50 .2h1.6l8.7 23.6h-1.9zM50.8 2.6l-5 13.9h9.9L50.8 2.6zM80 23.8L66.3 3.3v20.5h-1.8V.2h1.8L80 20.7V.2h1.8v23.6H80zm23-2.1c-1.4 1.4-3.5 2.1-5.9 2.1h-8V.2h8c2.5 0 4.5.8 5.9 2.2 2.4 2.4 2.3 6.1 2.3 9.3 0 3.2.1 7.6-2.3 10m-1.2-18.1c-1.4-1.4-3.2-1.8-5.1-1.8H91v20.4h5.7c1.9 0 3.7-.4 5.1-1.8 1.9-1.9 1.8-6.1 1.8-8.7-.1-2.6.1-6.2-1.8-8.1z" fill="#ed1c24"/>
+  <path d="M110.8 23.8V.2h4.6v19.5h10.8v4.1h-15.4zm25.6-9.7v9.7h-4.6v-9.7L124.7.2h5l4.4 9.5 4.4-9.5h5l-7.1 13.9zm24 7.3c-1.7 1.7-3.7 2.6-6.4 2.6s-4.8-.9-6.4-2.6c-2.4-2.4-2.3-5.3-2.3-9.4 0-4.1-.1-7 2.3-9.4C149.3.9 151.3 0 154 0s4.7.9 6.4 2.6c2.4 2.4 2.4 5.3 2.4 9.4-.1 4.1 0 7.1-2.4 9.4m-3.5-16c-.7-.8-1.7-1.3-3-1.3s-2.3.5-3 1.3c-.9 1-1.1 2.1-1.1 6.6s.2 5.6 1.1 6.6c.7.8 1.7 1.3 3 1.3s2.3-.5 3-1.3c.9-1 1.2-2.1 1.2-6.6s-.3-5.6-1.2-6.6zm24.6 18.4l-9.4-14.5v14.5h-4.6V.2h4.1l9.4 14.5V.2h4.6v23.6h-4.1z" fill="#050607"/>
+  <path d="M159.4 33.2c0-.8 0-2-1.1-2s-1 1.5-1 2.3c0 .7.1 1.8 1 1.8 1 0 1.1-.7 1.1-2.1m-2-1.9c.3-.5.7-.7 1.2-.7.9 0 1.7.3 1.7 2.6 0 1.3 0 2.8-1.7 2.8-.5 0-.9-.2-1.2-.6v2.5h-.9v-7.3h.9v.7zm-61.1 3.1c0 .5.2 1 .8 1 .5 0 1.2-.3 1.1-2-.8-.1-1.9-.1-1.9 1m1.9.8c-.3.6-.7.9-1.3.9-1.1 0-1.4-.7-1.4-1.7 0-1.6 1.6-1.7 2.8-1.6 0-.7 0-1.5-.9-1.5-.6 0-.9.4-.8.9h-.9c0-1.2.7-1.6 1.8-1.6 1.4 0 1.7.7 1.7 1.6v2.5c0 .4 0 .8.1 1.3h-.9l-.2-.8zm15.4.7v-3.7c0-.5-.2-1-.8-1-.3 0-.6.2-.7.4-.2.3-.2.6-.2.8V36h-.9v-4.2c0-.4 0-.7-.1-1.1h.9v.7c.2-.6.7-.8 1.3-.8.5 0 1 .2 1.2.7.3-.6.8-.7 1.3-.7.6 0 1.4.1 1.4 1.4v4h-.9v-3.7c0-.5-.2-1-.8-1-.3 0-.4 0-.7.2-.2.2-.3.7-.3.9V36c.2-.1-.7-.1-.7-.1zm12.1-7.5l-1.4 1.5h-.7l.9-1.5h1.2zm-.2 4.5c0-1.5-.4-1.7-1.1-1.7-.6 0-1 .3-1 1.7h2.1zm-2.1.7c0 1.5.4 1.8 1.1 1.8.6 0 .9-.5.9-1h.9c0 1.2-.7 1.7-1.8 1.7s-2-.3-2-2.7c0-1.5.2-2.8 2-2.8 1.5 0 1.9.8 1.9 2.6v.4h-3zm8.8-2.9v-1l.9-.4v1.4h1.1v.6H133v3.2c0 .3 0 .8.8.8h.4v.7c-.3 0-.6.1-.9.1-.8 0-1.2-.3-1.2-.9v-3.7h-.9v-.6l1-.2zm8.4.8c.4-.8.8-.9 1.7-.9v.9h-.4c-.9 0-1.2.7-1.2 1.4V36h-.9v-5.3h.9l-.1.8zm9.7 1.5c0-1.4-.4-1.8-1.1-1.8-.7 0-1.1.3-1.1 1.8 0 1.7.2 2.3 1.1 2.3.8 0 1.1-.6 1.1-2.3m-3.1.4c0-1.5.2-2.8 2-2.8s2 1.3 2 2.8c0 2.3-.9 2.7-2 2.7-1.2-.1-2-.4-2-2.7zm21.5-.4c0-1.4-.4-1.8-1.1-1.8s-1.1.3-1.1 1.8c0 1.7.2 2.3 1.1 2.3.8 0 1.1-.6 1.1-2.3m-3.1.4c0-1.5.2-2.8 2-2.8s2 1.3 2 2.8c0 2.3-.9 2.7-2 2.7-1.2-.1-2-.4-2-2.7zm10.4 2.5h-.9v-7.5h.9v7.5zm8.4-3c0-1.5-.4-1.7-1.1-1.7-.5 0-1 .3-1 1.7h2.1zm-2.1.7c0 1.5.4 1.8 1.1 1.8.6 0 .9-.5.9-1h.9c0 1.2-.7 1.7-1.8 1.7s-2-.3-2-2.7c0-1.5.2-2.8 2-2.8 1.5 0 1.9.8 1.9 2.6v.4h-3zm-91.8 2.3h-.9v-7.5h.9v7.5z" fill="#231f20"/>
+</svg>
diff --git a/webapp/src/scss/_functions.scss b/webapp/src/scss/_functions.scss
new file mode 100644
index 00000000..1266d34b
--- /dev/null
+++ b/webapp/src/scss/_functions.scss
@@ -0,0 +1,86 @@
+// Bootstrap functions
+//
+// Utility mixins and functions for evalutating source code across our variables, maps, and mixins.
+
+// Ascending
+// Used to evaluate Sass maps like our grid breakpoints.
+@mixin _assert-ascending($map, $map-name) {
+  $prev-key: null;
+  $prev-num: null;
+  @each $key, $num in $map {
+    @if $prev-num == null {
+      // Do nothing
+    } @else if not comparable($prev-num, $num) {
+      @warn "Potentially invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} whose unit makes it incomparable to #{$prev-num}, the value of the previous key '#{$prev-key}' !";
+    } @else if $prev-num >= $num {
+      @warn "Invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} which isn't greater than #{$prev-num}, the value of the previous key '#{$prev-key}' !";
+    }
+    $prev-key: $key;
+    $prev-num: $num;
+  }
+}
+
+// Starts at zero
+// Another grid mixin that ensures the min-width of the lowest breakpoint starts at 0.
+@mixin _assert-starts-at-zero($map) {
+  $values: map-values($map);
+  $first-value: nth($values, 1);
+  @if $first-value != 0 {
+    @warn "First breakpoint in `$grid-breakpoints` must start at 0, but starts at #{$first-value}.";
+  }
+}
+
+// Replace `$search` with `$replace` in `$string`
+// Used on our SVG icon backgrounds for custom forms.
+//
+// @author Hugo Giraudel
+// @param {String} $string - Initial string
+// @param {String} $search - Substring to replace
+// @param {String} $replace ('') - New value
+// @return {String} - Updated string
+@function str-replace($string, $search, $replace: "") {
+  $index: str-index($string, $search);
+
+  @if $index {
+    @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
+  }
+
+  @return $string;
+}
+
+// Color contrast
+@function color-yiq($color) {
+  $r: red($color);
+  $g: green($color);
+  $b: blue($color);
+
+  $yiq: (($r * 299) + ($g * 587) + ($b * 114)) / 1000;
+
+  @if ($yiq >= $yiq-contrasted-threshold) {
+    @return $yiq-text-dark;
+  } @else {
+    @return $yiq-text-light;
+  }
+}
+
+// Retrieve color Sass maps
+@function color($key: "blue") {
+  @return map-get($colors, $key);
+}
+
+@function theme-color($key: "primary") {
+  @return map-get($theme-colors, $key);
+}
+
+@function gray($key: "100") {
+  @return map-get($grays, $key);
+}
+
+// Request a theme color level
+@function theme-color-level($color-name: "primary", $level: 0) {
+  $color: theme-color($color-name);
+  $color-base: if($level > 0, #000, #fff);
+  $level: abs($level);
+
+  @return mix($color-base, $color, $level * $theme-color-interval);
+}
diff --git a/webapp/src/scss/_grid.scss b/webapp/src/scss/_grid.scss
new file mode 100644
index 00000000..a2275153
--- /dev/null
+++ b/webapp/src/scss/_grid.scss
@@ -0,0 +1,52 @@
+// Container widths
+//
+// Set the container width, and override it for fixed navbars in media queries.
+
+@if $enable-grid-classes {
+  .container {
+    @include make-container();
+    @include make-container-max-widths();
+  }
+}
+
+// Fluid container
+//
+// Utilizes the mixin meant for fixed width containers, but with 100% width for
+// fluid, full width layouts.
+
+@if $enable-grid-classes {
+  .container-fluid {
+    @include make-container();
+  }
+}
+
+// Row
+//
+// Rows contain and clear the floats of your columns.
+
+@if $enable-grid-classes {
+  .row {
+    @include make-row();
+  }
+
+  // Remove the negative margin from default .row, then the horizontal padding
+  // from all immediate children columns (to prevent runaway style inheritance).
+  .no-gutters {
+    margin-right: 0;
+    margin-left: 0;
+
+    > .col,
+    > [class*="col-"] {
+      padding-right: 0;
+      padding-left: 0;
+    }
+  }
+}
+
+// Columns
+//
+// Common styles for small and large grid columns
+
+@if $enable-grid-classes {
+  @include make-grid-columns();
+}
diff --git a/webapp/src/scss/_variables.scss b/webapp/src/scss/_variables.scss
new file mode 100644
index 00000000..be580deb
--- /dev/null
+++ b/webapp/src/scss/_variables.scss
@@ -0,0 +1,894 @@
+// Variables
+//
+// Variables should follow the `$component-state-property-size` formula for
+// consistent naming. Ex: $nav-link-disabled-color and $modal-content-box-shadow-xs.
+
+
+//
+// Color system
+//
+
+// stylelint-disable
+$white:    #fff !default;
+$gray-100: #f8f9fa !default;
+$gray-200: #e9ecef !default;
+$gray-300: #dee2e6 !default;
+$gray-400: #ced4da !default;
+$gray-500: #adb5bd !default;
+$gray-600: #6c757d !default;
+$gray-700: #495057 !default;
+$gray-800: #343a40 !default;
+$gray-900: #212529 !default;
+$black:    #000 !default;
+
+$grays: () !default;
+$grays: map-merge((
+  "100": $gray-100,
+  "200": $gray-200,
+  "300": $gray-300,
+  "400": $gray-400,
+  "500": $gray-500,
+  "600": $gray-600,
+  "700": $gray-700,
+  "800": $gray-800,
+  "900": $gray-900
+), $grays);
+
+$blue:    #007bff !default;
+$indigo:  #6610f2 !default;
+$purple:  #6f42c1 !default;
+$pink:    #e83e8c !default;
+$red:     #dc3545 !default;
+$orange:  #fd7e14 !default;
+$yellow:  #ffc107 !default;
+$green:   #28a745 !default;
+$teal:    #20c997 !default;
+$cyan:    #17a2b8 !default;
+
+$colors: () !default;
+$colors: map-merge((
+  "blue":       $blue,
+  "indigo":     $indigo,
+  "purple":     $purple,
+  "pink":       $pink,
+  "red":        $red,
+  "orange":     $orange,
+  "yellow":     $yellow,
+  "green":      $green,
+  "teal":       $teal,
+  "cyan":       $cyan,
+  "white":      $white,
+  "gray":       $gray-600,
+  "gray-dark":  $gray-800
+), $colors);
+
+$primary:       $blue !default;
+$secondary:     $gray-600 !default;
+$success:       $green !default;
+$info:          $cyan !default;
+$warning:       $yellow !default;
+$danger:        $red !default;
+$light:         $gray-100 !default;
+$dark:          $gray-800 !default;
+
+$theme-colors: () !default;
+$theme-colors: map-merge((
+  "primary":    $primary,
+  "secondary":  $secondary,
+  "success":    $success,
+  "info":       $info,
+  "warning":    $warning,
+  "danger":     $danger,
+  "light":      $light,
+  "dark":       $dark
+), $theme-colors);
+// stylelint-enable
+
+// Set a specific jump point for requesting color jumps
+$theme-color-interval:      8% !default;
+
+// The yiq lightness value that determines when the lightness of color changes from "dark" to "light". Acceptable values are between 0 and 255.
+$yiq-contrasted-threshold: 150 !default;
+
+// Customize the light and dark text colors for use in our YIQ color contrast function.
+$yiq-text-dark: $gray-900 !default;
+$yiq-text-light: $white !default;
+
+// Options
+//
+// Quickly modify global styling by enabling or disabling optional features.
+
+$enable-caret:              true !default;
+$enable-rounded:            true !default;
+$enable-shadows:            false !default;
+$enable-gradients:          false !default;
+$enable-transitions:        true !default;
+$enable-hover-media-query:  false !default; // Deprecated, no longer affects any compiled CSS
+$enable-grid-classes:       true !default;
+$enable-print-styles:       true !default;
+
+
+// Spacing
+//
+// Control the default styling of most Bootstrap elements by modifying these
+// variables. Mostly focused on spacing.
+// You can add more entries to the $spacers map, should you need more variation.
+
+// stylelint-disable
+$spacer: 1rem !default;
+$spacers: () !default;
+$spacers: map-merge((
+  0: 0,
+  1: ($spacer * .25),
+  2: ($spacer * .5),
+  3: $spacer,
+  4: ($spacer * 1.5),
+  5: ($spacer * 3)
+), $spacers);
+
+// This variable affects the `.h-*` and `.w-*` classes.
+$sizes: () !default;
+$sizes: map-merge((
+  25: 25%,
+  50: 50%,
+  75: 75%,
+  100: 100%
+), $sizes);
+// stylelint-enable
+
+// Body
+//
+// Settings for the `<body>` element.
+
+$body-bg:                   $white !default;
+$body-color:                $gray-900 !default;
+
+// Links
+//
+// Style anchor elements.
+
+$link-color:                theme-color("primary") !default;
+$link-decoration:           none !default;
+$link-hover-color:          darken($link-color, 15%) !default;
+$link-hover-decoration:     underline !default;
+
+// Paragraphs
+//
+// Style p element.
+
+$paragraph-margin-bottom:   1rem !default;
+
+
+// Grid breakpoints
+//
+// Define the minimum dimensions at which your layout will change,
+// adapting to different screen sizes, for use in media queries.
+
+$grid-breakpoints: (
+  xs: 0,
+  sm: 576px,
+  md: 768px,
+  lg: 992px,
+  xl: 1200px
+) !default;
+
+@include _assert-ascending($grid-breakpoints, "$grid-breakpoints");
+@include _assert-starts-at-zero($grid-breakpoints);
+
+
+// Grid containers
+//
+// Define the maximum width of `.container` for different screen sizes.
+
+$container-max-widths: (
+  sm: 540px,
+  md: 720px,
+  lg: 960px,
+  xl: 1140px
+) !default;
+
+@include _assert-ascending($container-max-widths, "$container-max-widths");
+
+
+// Grid columns
+//
+// Set the number of columns and specify the width of the gutters.
+
+$grid-columns:                12 !default;
+$grid-gutter-width:           30px !default;
+
+// Components
+//
+// Define common padding and border radius sizes and more.
+
+$line-height-lg:              1.5 !default;
+$line-height-sm:              1.5 !default;
+
+$border-width:                1px !default;
+$border-color:                $gray-300 !default;
+
+$border-radius:               .25rem !default;
+$border-radius-lg:            .3rem !default;
+$border-radius-sm:            .2rem !default;
+
+$component-active-color:      $white !default;
+$component-active-bg:         theme-color("primary") !default;
+
+$caret-width:                 .3em !default;
+
+$transition-base:             all .2s ease-in-out !default;
+$transition-fade:             opacity .15s linear !default;
+$transition-collapse:         height .35s ease !default;
+
+
+// Fonts
+//
+// Font, line-height, and color for body text, headings, and more.
+
+// stylelint-disable value-keyword-case
+$font-family-sans-serif:      -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !default;
+$font-family-monospace:       SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !default;
+$font-family-base:            $font-family-sans-serif !default;
+// stylelint-enable value-keyword-case
+
+$font-size-base:              1rem !default; // Assumes the browser default, typically `16px`
+$font-size-lg:                ($font-size-base * 1.25) !default;
+$font-size-sm:                ($font-size-base * .875) !default;
+
+$font-weight-light:           300 !default;
+$font-weight-normal:          400 !default;
+$font-weight-bold:            700 !default;
+
+$font-weight-base:            $font-weight-normal !default;
+$line-height-base:            1.5 !default;
+
+$h1-font-size:                $font-size-base * 2.5 !default;
+$h2-font-size:                $font-size-base * 2 !default;
+$h3-font-size:                $font-size-base * 1.75 !default;
+$h4-font-size:                $font-size-base * 1.5 !default;
+$h5-font-size:                $font-size-base * 1.25 !default;
+$h6-font-size:                $font-size-base !default;
+
+$headings-margin-bottom:      ($spacer / 2) !default;
+$headings-font-family:        inherit !default;
+$headings-font-weight:        500 !default;
+$headings-line-height:        1.2 !default;
+$headings-color:              inherit !default;
+
+$display1-size:               6rem !default;
+$display2-size:               5.5rem !default;
+$display3-size:               4.5rem !default;
+$display4-size:               3.5rem !default;
+
+$display1-weight:             300 !default;
+$display2-weight:             300 !default;
+$display3-weight:             300 !default;
+$display4-weight:             300 !default;
+$display-line-height:         $headings-line-height !default;
+
+$lead-font-size:              ($font-size-base * 1.25) !default;
+$lead-font-weight:            300 !default;
+
+$small-font-size:             80% !default;
+
+$text-muted:                  $gray-600 !default;
+
+$blockquote-small-color:      $gray-600 !default;
+$blockquote-font-size:        ($font-size-base * 1.25) !default;
+
+$hr-border-color:             rgba($black, .1) !default;
+$hr-border-width:             $border-width !default;
+
+$mark-padding:                .2em !default;
+
+$dt-font-weight:              $font-weight-bold !default;
+
+$kbd-box-shadow:              inset 0 -.1rem 0 rgba($black, .25) !default;
+$nested-kbd-font-weight:      $font-weight-bold !default;
+
+$list-inline-padding:         .5rem !default;
+
+$mark-bg:                     #fcf8e3 !default;
+
+$hr-margin-y:                 $spacer !default;
+
+
+// Tables
+//
+// Customizes the `.table` component with basic values, each used across all table variations.
+
+$table-cell-padding:          .75rem !default;
+$table-cell-padding-sm:       .3rem !default;
+
+$table-bg:                    transparent !default;
+$table-accent-bg:             rgba($black, .05) !default;
+$table-hover-bg:              rgba($black, .075) !default;
+$table-active-bg:             $table-hover-bg !default;
+
+$table-border-width:          $border-width !default;
+$table-border-color:          $gray-300 !default;
+
+$table-head-bg:               $gray-200 !default;
+$table-head-color:            $gray-700 !default;
+
+$table-dark-bg:               $gray-900 !default;
+$table-dark-accent-bg:        rgba($white, .05) !default;
+$table-dark-hover-bg:         rgba($white, .075) !default;
+$table-dark-border-color:     lighten($gray-900, 7.5%) !default;
+$table-dark-color:            $body-bg !default;
+
+
+// Buttons + Forms
+//
+// Shared variables that are reassigned to `$input-` and `$btn-` specific variables.
+
+$input-btn-padding-y:         .375rem !default;
+$input-btn-padding-x:         .75rem !default;
+$input-btn-line-height:       $line-height-base !default;
+
+$input-btn-focus-width:       .2rem !default;
+$input-btn-focus-color:       rgba($component-active-bg, .25) !default;
+$input-btn-focus-box-shadow:  0 0 0 $input-btn-focus-width $input-btn-focus-color !default;
+
+$input-btn-padding-y-sm:      .25rem !default;
+$input-btn-padding-x-sm:      .5rem !default;
+$input-btn-line-height-sm:    $line-height-sm !default;
+
+$input-btn-padding-y-lg:      .5rem !default;
+$input-btn-padding-x-lg:      1rem !default;
+$input-btn-line-height-lg:    $line-height-lg !default;
+
+$input-btn-border-width:      $border-width !default;
+
+
+// Buttons
+//
+// For each of Bootstrap's buttons, define text, background, and border color.
+
+$btn-padding-y:               $input-btn-padding-y !default;
+$btn-padding-x:               $input-btn-padding-x !default;
+$btn-line-height:             $input-btn-line-height !default;
+
+$btn-padding-y-sm:            $input-btn-padding-y-sm !default;
+$btn-padding-x-sm:            $input-btn-padding-x-sm !default;
+$btn-line-height-sm:          $input-btn-line-height-sm !default;
+
+$btn-padding-y-lg:            $input-btn-padding-y-lg !default;
+$btn-padding-x-lg:            $input-btn-padding-x-lg !default;
+$btn-line-height-lg:          $input-btn-line-height-lg !default;
+
+$btn-border-width:            $input-btn-border-width !default;
+
+$btn-font-weight:             $font-weight-normal !default;
+$btn-box-shadow:              inset 0 1px 0 rgba($white, .15), 0 1px 1px rgba($black, .075) !default;
+$btn-focus-width:             $input-btn-focus-width !default;
+$btn-focus-box-shadow:        $input-btn-focus-box-shadow !default;
+$btn-disabled-opacity:        .65 !default;
+$btn-active-box-shadow:       inset 0 3px 5px rgba($black, .125) !default;
+
+$btn-link-disabled-color:     $gray-600 !default;
+
+$btn-block-spacing-y:         .5rem !default;
+
+// Allows for customizing button radius independently from global border radius
+$btn-border-radius:           $border-radius !default;
+$btn-border-radius-lg:        $border-radius-lg !default;
+$btn-border-radius-sm:        $border-radius-sm !default;
+
+$btn-transition:              color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;
+
+
+// Forms
+
+$input-padding-y:                       $input-btn-padding-y !default;
+$input-padding-x:                       $input-btn-padding-x !default;
+$input-line-height:                     $input-btn-line-height !default;
+
+$input-padding-y-sm:                    $input-btn-padding-y-sm !default;
+$input-padding-x-sm:                    $input-btn-padding-x-sm !default;
+$input-line-height-sm:                  $input-btn-line-height-sm !default;
+
+$input-padding-y-lg:                    $input-btn-padding-y-lg !default;
+$input-padding-x-lg:                    $input-btn-padding-x-lg !default;
+$input-line-height-lg:                  $input-btn-line-height-lg !default;
+
+$input-bg:                              $white !default;
+$input-disabled-bg:                     $gray-200 !default;
+
+$input-color:                           $gray-700 !default;
+$input-border-color:                    $gray-400 !default;
+$input-border-width:                    $input-btn-border-width !default;
+$input-box-shadow:                      inset 0 1px 1px rgba($black, .075) !default;
+
+$input-border-radius:                   $border-radius !default;
+$input-border-radius-lg:                $border-radius-lg !default;
+$input-border-radius-sm:                $border-radius-sm !default;
+
+$input-focus-bg:                        $input-bg !default;
+$input-focus-border-color:              lighten($component-active-bg, 25%) !default;
+$input-focus-color:                     $input-color !default;
+$input-focus-width:                     $input-btn-focus-width !default;
+$input-focus-box-shadow:                $input-btn-focus-box-shadow !default;
+
+$input-placeholder-color:               $gray-600 !default;
+
+$input-height-border:                   $input-border-width * 2 !default;
+
+$input-height-inner:                    ($font-size-base * $input-btn-line-height) + ($input-btn-padding-y * 2) !default;
+$input-height:                          calc(#{$input-height-inner} + #{$input-height-border}) !default;
+
+$input-height-inner-sm:                 ($font-size-sm * $input-btn-line-height-sm) + ($input-btn-padding-y-sm * 2) !default;
+$input-height-sm:                       calc(#{$input-height-inner-sm} + #{$input-height-border}) !default;
+
+$input-height-inner-lg:                 ($font-size-lg * $input-btn-line-height-lg) + ($input-btn-padding-y-lg * 2) !default;
+$input-height-lg:                       calc(#{$input-height-inner-lg} + #{$input-height-border}) !default;
+
+$input-transition:                      border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;
+
+$form-text-margin-top:                  .25rem !default;
+
+$form-check-input-gutter:               1.25rem !default;
+$form-check-input-margin-y:             .3rem !default;
+$form-check-input-margin-x:             .25rem !default;
+
+$form-check-inline-margin-x:            .75rem !default;
+$form-check-inline-input-margin-x:      .3125rem !default;
+
+$form-group-margin-bottom:              1rem !default;
+
+$input-group-addon-color:               $input-color !default;
+$input-group-addon-bg:                  $gray-200 !default;
+$input-group-addon-border-color:        $input-border-color !default;
+
+$custom-control-gutter:                 1.5rem !default;
+$custom-control-spacer-x:               1rem !default;
+
+$custom-control-indicator-size:         1rem !default;
+$custom-control-indicator-bg:           $gray-300 !default;
+$custom-control-indicator-bg-size:      50% 50% !default;
+$custom-control-indicator-box-shadow:   inset 0 .25rem .25rem rgba($black, .1) !default;
+
+$custom-control-indicator-disabled-bg:          $gray-200 !default;
+$custom-control-label-disabled-color:     $gray-600 !default;
+
+$custom-control-indicator-checked-color:        $component-active-color !default;
+$custom-control-indicator-checked-bg:           $component-active-bg !default;
+$custom-control-indicator-checked-disabled-bg:  rgba(theme-color("primary"), .5) !default;
+$custom-control-indicator-checked-box-shadow:   none !default;
+
+$custom-control-indicator-focus-box-shadow:     0 0 0 1px $body-bg, $input-btn-focus-box-shadow !default;
+
+$custom-control-indicator-active-color:         $component-active-color !default;
+$custom-control-indicator-active-bg:            lighten($component-active-bg, 35%) !default;
+$custom-control-indicator-active-box-shadow:    none !default;
+
+$custom-checkbox-indicator-border-radius:       $border-radius !default;
+$custom-checkbox-indicator-icon-checked:        str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='#{$custom-control-indicator-checked-color}' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E"), "#", "%23") !default;
+
+$custom-checkbox-indicator-indeterminate-bg:    $component-active-bg !default;
+$custom-checkbox-indicator-indeterminate-color: $custom-control-indicator-checked-color !default;
+$custom-checkbox-indicator-icon-indeterminate:  str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='#{$custom-checkbox-indicator-indeterminate-color}' d='M0 2h4'/%3E%3C/svg%3E"), "#", "%23") !default;
+$custom-checkbox-indicator-indeterminate-box-shadow: none !default;
+
+$custom-radio-indicator-border-radius:          50% !default;
+$custom-radio-indicator-icon-checked:           str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='#{$custom-control-indicator-checked-color}'/%3E%3C/svg%3E"), "#", "%23") !default;
+
+$custom-select-padding-y:           .375rem !default;
+$custom-select-padding-x:          .75rem !default;
+$custom-select-height:              $input-height !default;
+$custom-select-indicator-padding:   1rem !default; // Extra padding to account for the presence of the background-image based indicator
+$custom-select-line-height:         $input-btn-line-height !default;
+$custom-select-color:               $input-color !default;
+$custom-select-disabled-color:      $gray-600 !default;
+$custom-select-bg:                  $white !default;
+$custom-select-disabled-bg:         $gray-200 !default;
+$custom-select-bg-size:             8px 10px !default; // In pixels because image dimensions
+$custom-select-indicator-color:     $gray-800 !default;
+$custom-select-indicator:           str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='#{$custom-select-indicator-color}' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E"), "#", "%23") !default;
+$custom-select-border-width:        $input-btn-border-width !default;
+$custom-select-border-color:        $input-border-color !default;
+$custom-select-border-radius:       $border-radius !default;
+
+$custom-select-focus-border-color:  $input-focus-border-color !default;
+$custom-select-focus-box-shadow:    inset 0 1px 2px rgba($black, .075), 0 0 5px rgba($custom-select-focus-border-color, .5) !default;
+
+$custom-select-font-size-sm:        75% !default;
+$custom-select-height-sm:           $input-height-sm !default;
+
+$custom-select-font-size-lg:        125% !default;
+$custom-select-height-lg:           $input-height-lg !default;
+
+$custom-file-height:                $input-height !default;
+$custom-file-focus-border-color:    $input-focus-border-color !default;
+$custom-file-focus-box-shadow:      $input-btn-focus-box-shadow !default;
+
+$custom-file-padding-y:             $input-btn-padding-y !default;
+$custom-file-padding-x:             $input-btn-padding-x !default;
+$custom-file-line-height:           $input-btn-line-height !default;
+$custom-file-color:                 $input-color !default;
+$custom-file-bg:                    $input-bg !default;
+$custom-file-border-width:          $input-btn-border-width !default;
+$custom-file-border-color:          $input-border-color !default;
+$custom-file-border-radius:         $input-border-radius !default;
+$custom-file-box-shadow:            $input-box-shadow !default;
+$custom-file-button-color:          $custom-file-color !default;
+$custom-file-button-bg:             $input-group-addon-bg !default;
+$custom-file-text: (
+  en: "Browse"
+) !default;
+
+
+// Form validation
+$form-feedback-margin-top:          $form-text-margin-top !default;
+$form-feedback-font-size:           $small-font-size !default;
+$form-feedback-valid-color:         theme-color("success") !default;
+$form-feedback-invalid-color:       theme-color("danger") !default;
+
+
+// Dropdowns
+//
+// Dropdown menu container and contents.
+
+$dropdown-min-width:                10rem !default;
+$dropdown-padding-y:                .5rem !default;
+$dropdown-spacer:                   .125rem !default;
+$dropdown-bg:                       $white !default;
+$dropdown-border-color:             rgba($black, .15) !default;
+$dropdown-border-radius:            $border-radius !default;
+$dropdown-border-width:             $border-width !default;
+$dropdown-divider-bg:               $gray-200 !default;
+$dropdown-box-shadow:               0 .5rem 1rem rgba($black, .175) !default;
+
+$dropdown-link-color:               $gray-900 !default;
+$dropdown-link-hover-color:         darken($gray-900, 5%) !default;
+$dropdown-link-hover-bg:            $gray-100 !default;
+
+$dropdown-link-active-color:        $component-active-color !default;
+$dropdown-link-active-bg:           $component-active-bg !default;
+
+$dropdown-link-disabled-color:      $gray-600 !default;
+
+$dropdown-item-padding-y:           .25rem !default;
+$dropdown-item-padding-x:           1.5rem !default;
+
+$dropdown-header-color:             $gray-600 !default;
+
+
+// Z-index master list
+//
+// Warning: Avoid customizing these values. They're used for a bird's eye view
+// of components dependent on the z-axis and are designed to all work together.
+
+$zindex-dropdown:                   1000 !default;
+$zindex-sticky:                     1020 !default;
+$zindex-fixed:                      1030 !default;
+$zindex-modal-backdrop:             1040 !default;
+$zindex-modal:                      1050 !default;
+$zindex-popover:                    1060 !default;
+$zindex-tooltip:                    1070 !default;
+
+// Navs
+
+$nav-link-padding-y:                .5rem !default;
+$nav-link-padding-x:                1rem !default;
+$nav-link-disabled-color:           $gray-600 !default;
+
+$nav-tabs-border-color:             $gray-300 !default;
+$nav-tabs-border-width:             $border-width !default;
+$nav-tabs-border-radius:            $border-radius !default;
+$nav-tabs-link-hover-border-color:  $gray-200 $gray-200 $nav-tabs-border-color !default;
+$nav-tabs-link-active-color:        $gray-700 !default;
+$nav-tabs-link-active-bg:           $body-bg !default;
+$nav-tabs-link-active-border-color: $gray-300 $gray-300 $nav-tabs-link-active-bg !default;
+
+$nav-pills-border-radius:           $border-radius !default;
+$nav-pills-link-active-color:       $component-active-color !default;
+$nav-pills-link-active-bg:          $component-active-bg !default;
+
+// Navbar
+
+$navbar-padding-y:                  ($spacer / 2) !default;
+$navbar-padding-x:                  $spacer !default;
+
+$navbar-nav-link-padding-x:         .5rem !default;
+
+$navbar-brand-font-size:            $font-size-lg !default;
+// Compute the navbar-brand padding-y so the navbar-brand will have the same height as navbar-text and nav-link
+$nav-link-height:                   ($font-size-base * $line-height-base + $nav-link-padding-y * 2) !default;
+$navbar-brand-height:               $navbar-brand-font-size * $line-height-base !default;
+$navbar-brand-padding-y:            ($nav-link-height - $navbar-brand-height) / 2 !default;
+
+$navbar-toggler-padding-y:          .25rem !default;
+$navbar-toggler-padding-x:          .75rem !default;
+$navbar-toggler-font-size:          $font-size-lg !default;
+$navbar-toggler-border-radius:      $btn-border-radius !default;
+
+$navbar-dark-color:                 rgba($white, .5) !default;
+$navbar-dark-hover-color:           rgba($white, .75) !default;
+$navbar-dark-active-color:          $white !default;
+$navbar-dark-disabled-color:        rgba($white, .25) !default;
+$navbar-dark-toggler-icon-bg:       str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#{$navbar-dark-color}' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"), "#", "%23") !default;
+$navbar-dark-toggler-border-color:  rgba($white, .1) !default;
+
+$navbar-light-color:                rgba($black, .5) !default;
+$navbar-light-hover-color:          rgba($black, .7) !default;
+$navbar-light-active-color:         rgba($black, .9) !default;
+$navbar-light-disabled-color:       rgba($black, .3) !default;
+$navbar-light-toggler-icon-bg:      str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#{$navbar-light-color}' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"), "#", "%23") !default;
+$navbar-light-toggler-border-color: rgba($black, .1) !default;
+
+// Pagination
+
+$pagination-padding-y:              .5rem !default;
+$pagination-padding-x:              .75rem !default;
+$pagination-padding-y-sm:           .25rem !default;
+$pagination-padding-x-sm:           .5rem !default;
+$pagination-padding-y-lg:           .75rem !default;
+$pagination-padding-x-lg:           1.5rem !default;
+$pagination-line-height:            1.25 !default;
+
+$pagination-color:                  $link-color !default;
+$pagination-bg:                     $white !default;
+$pagination-border-width:           $border-width !default;
+$pagination-border-color:           $gray-300 !default;
+
+$pagination-focus-box-shadow:       $input-btn-focus-box-shadow !default;
+
+$pagination-hover-color:            $link-hover-color !default;
+$pagination-hover-bg:               $gray-200 !default;
+$pagination-hover-border-color:     $gray-300 !default;
+
+$pagination-active-color:           $component-active-color !default;
+$pagination-active-bg:              $component-active-bg !default;
+$pagination-active-border-color:    $pagination-active-bg !default;
+
+$pagination-disabled-color:         $gray-600 !default;
+$pagination-disabled-bg:            $white !default;
+$pagination-disabled-border-color:  $gray-300 !default;
+
+
+// Jumbotron
+
+$jumbotron-padding:                 2rem !default;
+$jumbotron-bg:                      $gray-200 !default;
+
+
+// Cards
+
+$card-spacer-y:                     .75rem !default;
+$card-spacer-x:                     1.25rem !default;
+$card-border-width:                 $border-width !default;
+$card-border-radius:                $border-radius !default;
+$card-border-color:                 rgba($black, .125) !default;
+$card-inner-border-radius:          calc(#{$card-border-radius} - #{$card-border-width}) !default;
+$card-cap-bg:                       rgba($black, .03) !default;
+$card-bg:                           $white !default;
+
+$card-img-overlay-padding:          1.25rem !default;
+
+$card-group-margin:                 ($grid-gutter-width / 2) !default;
+$card-deck-margin:                  $card-group-margin !default;
+
+$card-columns-count:                3 !default;
+$card-columns-gap:                  1.25rem !default;
+$card-columns-margin:               $card-spacer-y !default;
+
+
+// Tooltips
+
+$tooltip-font-size:           $font-size-sm !default;
+$tooltip-max-width:           200px !default;
+$tooltip-color:               $white !default;
+$tooltip-bg:                  $black !default;
+$tooltip-border-radius:        $border-radius !default;
+$tooltip-opacity:             .9 !default;
+$tooltip-padding-y:           .25rem !default;
+$tooltip-padding-x:           .5rem !default;
+$tooltip-margin:              0 !default;
+
+$tooltip-arrow-width:         .8rem !default;
+$tooltip-arrow-height:        .4rem !default;
+$tooltip-arrow-color:         $tooltip-bg !default;
+
+
+// Popovers
+
+$popover-font-size:                 $font-size-sm !default;
+$popover-bg:                        $white !default;
+$popover-max-width:                 276px !default;
+$popover-border-width:              $border-width !default;
+$popover-border-color:              rgba($black, .2) !default;
+$popover-border-radius:             $border-radius-lg !default;
+$popover-box-shadow:                0 .25rem .5rem rgba($black, .2) !default;
+
+$popover-header-bg:                 darken($popover-bg, 3%) !default;
+$popover-header-color:              $headings-color !default;
+$popover-header-padding-y:          .5rem !default;
+$popover-header-padding-x:          .75rem !default;
+
+$popover-body-color:                $body-color !default;
+$popover-body-padding-y:            $popover-header-padding-y !default;
+$popover-body-padding-x:            $popover-header-padding-x !default;
+
+$popover-arrow-width:               1rem !default;
+$popover-arrow-height:              .5rem !default;
+$popover-arrow-color:               $popover-bg !default;
+
+$popover-arrow-outer-color:         fade-in($popover-border-color, .05) !default;
+
+
+// Badges
+
+$badge-font-size:                   75% !default;
+$badge-font-weight:                 $font-weight-bold !default;
+$badge-padding-y:                   .25em !default;
+$badge-padding-x:                   .4em !default;
+$badge-border-radius:               $border-radius !default;
+
+$badge-pill-padding-x:              .6em !default;
+// Use a higher than normal value to ensure completely rounded edges when
+// customizing padding or font-size on labels.
+$badge-pill-border-radius:          10rem !default;
+
+
+// Modals
+
+// Padding applied to the modal body
+$modal-inner-padding:         1rem !default;
+
+$modal-dialog-margin:         .5rem !default;
+$modal-dialog-margin-y-sm-up: 1.75rem !default;
+
+$modal-title-line-height:           $line-height-base !default;
+
+$modal-content-bg:               $white !default;
+$modal-content-border-color:     rgba($black, .2) !default;
+$modal-content-border-width:     $border-width !default;
+$modal-content-box-shadow-xs:    0 .25rem .5rem rgba($black, .5) !default;
+$modal-content-box-shadow-sm-up: 0 .5rem 1rem rgba($black, .5) !default;
+
+$modal-backdrop-bg:           $black !default;
+$modal-backdrop-opacity:      .5 !default;
+$modal-header-border-color:   $gray-200 !default;
+$modal-footer-border-color:   $modal-header-border-color !default;
+$modal-header-border-width:   $modal-content-border-width !default;
+$modal-footer-border-width:   $modal-header-border-width !default;
+$modal-header-padding:        1rem !default;
+
+$modal-lg:                          800px !default;
+$modal-md:                          500px !default;
+$modal-sm:                          300px !default;
+
+$modal-transition:                  transform .3s ease-out !default;
+
+
+// Alerts
+//
+// Define alert colors, border radius, and padding.
+
+$alert-padding-y:                   .75rem !default;
+$alert-padding-x:                   1.25rem !default;
+$alert-margin-bottom:               1rem !default;
+$alert-border-radius:               $border-radius !default;
+$alert-link-font-weight:            $font-weight-bold !default;
+$alert-border-width:                $border-width !default;
+
+$alert-bg-level:                    -10 !default;
+$alert-border-level:                -9 !default;
+$alert-color-level:                 6 !default;
+
+
+// Progress bars
+
+$progress-height:                   1rem !default;
+$progress-font-size:                ($font-size-base * .75) !default;
+$progress-bg:                       $gray-200 !default;
+$progress-border-radius:            $border-radius !default;
+$progress-box-shadow:               inset 0 .1rem .1rem rgba($black, .1) !default;
+$progress-bar-color:                $white !default;
+$progress-bar-bg:                   theme-color("primary") !default;
+$progress-bar-animation-timing:     1s linear infinite !default;
+$progress-bar-transition:           width .6s ease !default;
+
+// List group
+
+$list-group-bg:                     $white !default;
+$list-group-border-color:           rgba($black, .125) !default;
+$list-group-border-width:           $border-width !default;
+$list-group-border-radius:          $border-radius !default;
+
+$list-group-item-padding-y:         .75rem !default;
+$list-group-item-padding-x:         1.25rem !default;
+
+$list-group-hover-bg:               $gray-100 !default;
+$list-group-active-color:           $component-active-color !default;
+$list-group-active-bg:              $component-active-bg !default;
+$list-group-active-border-color:    $list-group-active-bg !default;
+
+$list-group-disabled-color:         $gray-600 !default;
+$list-group-disabled-bg:            $list-group-bg !default;
+
+$list-group-action-color:           $gray-700 !default;
+$list-group-action-hover-color:     $list-group-action-color !default;
+
+$list-group-action-active-color:    $body-color !default;
+$list-group-action-active-bg:       $gray-200 !default;
+
+
+// Image thumbnails
+
+$thumbnail-padding:                 .25rem !default;
+$thumbnail-bg:                      $body-bg !default;
+$thumbnail-border-width:            $border-width !default;
+$thumbnail-border-color:            $gray-300 !default;
+$thumbnail-border-radius:           $border-radius !default;
+$thumbnail-box-shadow:              0 1px 2px rgba($black, .075) !default;
+
+
+// Figures
+
+$figure-caption-font-size:          90% !default;
+$figure-caption-color:              $gray-600 !default;
+
+
+// Breadcrumbs
+
+$breadcrumb-padding-y:              .75rem !default;
+$breadcrumb-padding-x:              1rem !default;
+$breadcrumb-item-padding:           .5rem !default;
+
+$breadcrumb-margin-bottom:          1rem !default;
+
+$breadcrumb-bg:                     $gray-200 !default;
+$breadcrumb-divider-color:          $gray-600 !default;
+$breadcrumb-active-color:           $gray-600 !default;
+$breadcrumb-divider:                "/" !default;
+
+
+// Carousel
+
+$carousel-control-color:            $white !default;
+$carousel-control-width:            15% !default;
+$carousel-control-opacity:          .5 !default;
+
+$carousel-indicator-width:          30px !default;
+$carousel-indicator-height:         3px !default;
+$carousel-indicator-spacer:         3px !default;
+$carousel-indicator-active-bg:      $white !default;
+
+$carousel-caption-width:            70% !default;
+$carousel-caption-color:            $white !default;
+
+$carousel-control-icon-width:       20px !default;
+
+$carousel-control-prev-icon-bg:     str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='#{$carousel-control-color}' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E"), "#", "%23") !default;
+$carousel-control-next-icon-bg:     str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='#{$carousel-control-color}' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E"), "#", "%23") !default;
+
+$carousel-transition:               transform .6s ease !default;
+
+
+// Close
+
+$close-font-size:                   $font-size-base * 1.5 !default;
+$close-font-weight:                 $font-weight-bold !default;
+$close-color:                       $black !default;
+$close-text-shadow:                 0 1px 0 $white !default;
+
+// Code
+
+$code-font-size:                    87.5% !default;
+$code-color:                        $pink !default;
+
+$kbd-padding-y:                     .2rem !default;
+$kbd-padding-x:                     .4rem !default;
+$kbd-font-size:                     $code-font-size !default;
+$kbd-color:                         $white !default;
+$kbd-bg:                            $gray-900 !default;
+
+$pre-color:                         $gray-900 !default;
+$pre-scrollable-max-height:         340px !default;
+
+
+// Printing
+$print-page-size:                   a3 !default;
+$print-body-min-width:              map-get($grid-breakpoints, "lg") !default;
diff --git a/webapp/src/scss/bootstrap-grid.scss b/webapp/src/scss/bootstrap-grid.scss
new file mode 100644
index 00000000..26c0dc89
--- /dev/null
+++ b/webapp/src/scss/bootstrap-grid.scss
@@ -0,0 +1,32 @@
+/*!
+ * Bootstrap Grid v4.0.0 (https://getbootstrap.com)
+ * Copyright 2011-2018 The Bootstrap Authors
+ * Copyright 2011-2018 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+
+@at-root {
+  @-ms-viewport { width: device-width; } // stylelint-disable-line at-rule-no-vendor-prefix
+}
+
+html {
+  box-sizing: border-box;
+  -ms-overflow-style: scrollbar;
+}
+
+*,
+*::before,
+*::after {
+  box-sizing: inherit;
+}
+
+@import "functions";
+@import "variables";
+
+@import "mixins/breakpoints";
+@import "mixins/grid-framework";
+@import "mixins/grid";
+
+@import "grid";
+@import "utilities/display";
+@import "utilities/flex";
diff --git a/webapp/src/scss/mixins/_breakpoints.scss b/webapp/src/scss/mixins/_breakpoints.scss
new file mode 100644
index 00000000..d1ad684c
--- /dev/null
+++ b/webapp/src/scss/mixins/_breakpoints.scss
@@ -0,0 +1,123 @@
+// Breakpoint viewport sizes and media queries.
+//
+// Breakpoints are defined as a map of (name: minimum width), order from small to large:
+//
+//    (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)
+//
+// The map defined in the `$grid-breakpoints` global variable is used as the `$breakpoints` argument by default.
+
+// Name of the next breakpoint, or null for the last breakpoint.
+//
+//    >> breakpoint-next(sm)
+//    md
+//    >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
+//    md
+//    >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl))
+//    md
+@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {
+  $n: index($breakpoint-names, $name);
+  @return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null);
+}
+
+// Minimum breakpoint width. Null for the smallest (first) breakpoint.
+//
+//    >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
+//    576px
+@function breakpoint-min($name, $breakpoints: $grid-breakpoints) {
+  $min: map-get($breakpoints, $name);
+  @return if($min != 0, $min, null);
+}
+
+// Maximum breakpoint width. Null for the largest (last) breakpoint.
+// The maximum value is calculated as the minimum of the next one less 0.02px
+// to work around the limitations of `min-` and `max-` prefixes and viewports with fractional widths.
+// See https://www.w3.org/TR/mediaqueries-4/#mq-min-max
+// Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari.
+// See https://bugs.webkit.org/show_bug.cgi?id=178261
+//
+//    >> breakpoint-max(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
+//    767.98px
+@function breakpoint-max($name, $breakpoints: $grid-breakpoints) {
+  $next: breakpoint-next($name, $breakpoints);
+  @return if($next, breakpoint-min($next, $breakpoints) - .02px, null);
+}
+
+// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash infront.
+// Useful for making responsive utilities.
+//
+//    >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
+//    ""  (Returns a blank string)
+//    >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
+//    "-sm"
+@function breakpoint-infix($name, $breakpoints: $grid-breakpoints) {
+  @return if(breakpoint-min($name, $breakpoints) == null, "", "-#{$name}");
+}
+
+// Media of at least the minimum breakpoint width. No query for the smallest breakpoint.
+// Makes the @content apply to the given breakpoint and wider.
+@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {
+  $min: breakpoint-min($name, $breakpoints);
+  @if $min {
+    @media (min-width: $min) {
+      @content;
+    }
+  } @else {
+    @content;
+  }
+}
+
+// Media of at most the maximum breakpoint width. No query for the largest breakpoint.
+// Makes the @content apply to the given breakpoint and narrower.
+@mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) {
+  $max: breakpoint-max($name, $breakpoints);
+  @if $max {
+    @media (max-width: $max) {
+      @content;
+    }
+  } @else {
+    @content;
+  }
+}
+
+// Media that spans multiple breakpoint widths.
+// Makes the @content apply between the min and max breakpoints
+@mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints) {
+  $min: breakpoint-min($lower, $breakpoints);
+  $max: breakpoint-max($upper, $breakpoints);
+
+  @if $min != null and $max != null {
+    @media (min-width: $min) and (max-width: $max) {
+      @content;
+    }
+  } @else if $max == null {
+    @include media-breakpoint-up($lower, $breakpoints) {
+      @content;
+    }
+  } @else if $min == null {
+    @include media-breakpoint-down($upper, $breakpoints) {
+      @content;
+    }
+  }
+}
+
+// Media between the breakpoint's minimum and maximum widths.
+// No minimum for the smallest breakpoint, and no maximum for the largest one.
+// Makes the @content apply only to the given breakpoint, not viewports any wider or narrower.
+@mixin media-breakpoint-only($name, $breakpoints: $grid-breakpoints) {
+  $min: breakpoint-min($name, $breakpoints);
+  $max: breakpoint-max($name, $breakpoints);
+
+  @if $min != null and $max != null {
+    @media (min-width: $min) and (max-width: $max) {
+      @content;
+    }
+  } @else if $max == null {
+    @include media-breakpoint-up($name, $breakpoints) {
+      @content;
+    }
+  } @else if $min == null {
+    @include media-breakpoint-down($name, $breakpoints) {
+      @content;
+    }
+  }
+}
diff --git a/webapp/src/scss/mixins/_grid-framework.scss b/webapp/src/scss/mixins/_grid-framework.scss
new file mode 100644
index 00000000..7b37f868
--- /dev/null
+++ b/webapp/src/scss/mixins/_grid-framework.scss
@@ -0,0 +1,67 @@
+// Framework grid generation
+//
+// Used only by Bootstrap to generate the correct number of grid classes given
+// any value of `$grid-columns`.
+
+@mixin make-grid-columns($columns: $grid-columns, $gutter: $grid-gutter-width, $breakpoints: $grid-breakpoints) {
+  // Common properties for all breakpoints
+  %grid-column {
+    position: relative;
+    width: 100%;
+    min-height: 1px; // Prevent columns from collapsing when empty
+    padding-right: ($gutter / 2);
+    padding-left: ($gutter / 2);
+  }
+
+  @each $breakpoint in map-keys($breakpoints) {
+    $infix: breakpoint-infix($breakpoint, $breakpoints);
+
+    // Allow columns to stretch full width below their breakpoints
+    @for $i from 1 through $columns {
+      .col#{$infix}-#{$i} {
+        @extend %grid-column;
+      }
+    }
+    .col#{$infix},
+    .col#{$infix}-auto {
+      @extend %grid-column;
+    }
+
+    @include media-breakpoint-up($breakpoint, $breakpoints) {
+      // Provide basic `.col-{bp}` classes for equal-width flexbox columns
+      .col#{$infix} {
+        flex-basis: 0;
+        flex-grow: 1;
+        max-width: 100%;
+      }
+      .col#{$infix}-auto {
+        flex: 0 0 auto;
+        width: auto;
+        max-width: none; // Reset earlier grid tiers
+      }
+
+      @for $i from 1 through $columns {
+        .col#{$infix}-#{$i} {
+          @include make-col($i, $columns);
+        }
+      }
+
+      .order#{$infix}-first { order: -1; }
+
+      .order#{$infix}-last { order: $columns + 1; }
+
+      @for $i from 0 through $columns {
+        .order#{$infix}-#{$i} { order: $i; }
+      }
+
+      // `$columns - 1` because offsetting by the width of an entire row isn't possible
+      @for $i from 0 through ($columns - 1) {
+        @if not ($infix == "" and $i == 0) { // Avoid emitting useless .offset-0
+          .offset#{$infix}-#{$i} {
+            @include make-col-offset($i, $columns);
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/webapp/src/scss/mixins/_grid.scss b/webapp/src/scss/mixins/_grid.scss
new file mode 100644
index 00000000..b75ebcbc
--- /dev/null
+++ b/webapp/src/scss/mixins/_grid.scss
@@ -0,0 +1,52 @@
+/// Grid system
+//
+// Generate semantic grid columns with these mixins.
+
+@mixin make-container() {
+  width: 100%;
+  padding-right: ($grid-gutter-width / 2);
+  padding-left: ($grid-gutter-width / 2);
+  margin-right: auto;
+  margin-left: auto;
+}
+
+
+// For each breakpoint, define the maximum width of the container in a media query
+@mixin make-container-max-widths($max-widths: $container-max-widths, $breakpoints: $grid-breakpoints) {
+  @each $breakpoint, $container-max-width in $max-widths {
+    @include media-breakpoint-up($breakpoint, $breakpoints) {
+      max-width: $container-max-width;
+    }
+  }
+}
+
+@mixin make-row() {
+  display: flex;
+  flex-wrap: wrap;
+  margin-right: ($grid-gutter-width / -2);
+  margin-left: ($grid-gutter-width / -2);
+}
+
+@mixin make-col-ready() {
+  position: relative;
+  // Prevent columns from becoming too narrow when at smaller grid tiers by
+  // always setting `width: 100%;`. This works because we use `flex` values
+  // later on to override this initial width.
+  width: 100%;
+  min-height: 1px; // Prevent collapsing
+  padding-right: ($grid-gutter-width / 2);
+  padding-left: ($grid-gutter-width / 2);
+}
+
+@mixin make-col($size, $columns: $grid-columns) {
+  flex: 0 0 percentage($size / $columns);
+  // Add a `max-width` to ensure content within each column does not blow out
+  // the width of the column. Applies to IE10+ and Firefox. Chrome and Safari
+  // do not appear to require this.
+  max-width: percentage($size / $columns);
+}
+
+@mixin make-col-offset($size, $columns: $grid-columns) {
+  $num: $size / $columns;
+  margin-left: if($num == 0, 0, percentage($num));
+}
diff --git a/webapp/src/scss/utilities/_display.scss b/webapp/src/scss/utilities/_display.scss
new file mode 100644
index 00000000..20aeeb5f
--- /dev/null
+++ b/webapp/src/scss/utilities/_display.scss
@@ -0,0 +1,38 @@
+// stylelint-disable declaration-no-important
+
+//
+// Utilities for common `display` values
+//
+
+@each $breakpoint in map-keys($grid-breakpoints) {
+  @include media-breakpoint-up($breakpoint) {
+    $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+
+    .d#{$infix}-none         { display: none !important; }
+    .d#{$infix}-inline       { display: inline !important; }
+    .d#{$infix}-inline-block { display: inline-block !important; }
+    .d#{$infix}-block        { display: block !important; }
+    .d#{$infix}-table        { display: table !important; }
+    .d#{$infix}-table-row    { display: table-row !important; }
+    .d#{$infix}-table-cell   { display: table-cell !important; }
+    .d#{$infix}-flex         { display: flex !important; }
+    .d#{$infix}-inline-flex  { display: inline-flex !important; }
+  }
+}
+
+
+//
+// Utilities for toggling `display` in print
+//
+
+@media print {
+  .d-print-none         { display: none !important; }
+  .d-print-inline       { display: inline !important; }
+  .d-print-inline-block { display: inline-block !important; }
+  .d-print-block        { display: block !important; }
+  .d-print-table        { display: table !important; }
+  .d-print-table-row    { display: table-row !important; }
+  .d-print-table-cell   { display: table-cell !important; }
+  .d-print-flex         { display: flex !important; }
+  .d-print-inline-flex  { display: inline-flex !important; }
+}
diff --git a/webapp/src/scss/utilities/_flex.scss b/webapp/src/scss/utilities/_flex.scss
new file mode 100644
index 00000000..8e470384
--- /dev/null
+++ b/webapp/src/scss/utilities/_flex.scss
@@ -0,0 +1,46 @@
+// stylelint-disable declaration-no-important
+
+// Flex variation
+//
+// Custom styles for additional flex alignment options.
+
+@each $breakpoint in map-keys($grid-breakpoints) {
+  @include media-breakpoint-up($breakpoint) {
+    $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+
+    .flex#{$infix}-row            { flex-direction: row !important; }
+    .flex#{$infix}-column         { flex-direction: column !important; }
+    .flex#{$infix}-row-reverse    { flex-direction: row-reverse !important; }
+    .flex#{$infix}-column-reverse { flex-direction: column-reverse !important; }
+
+    .flex#{$infix}-wrap         { flex-wrap: wrap !important; }
+    .flex#{$infix}-nowrap       { flex-wrap: nowrap !important; }
+    .flex#{$infix}-wrap-reverse { flex-wrap: wrap-reverse !important; }
+
+    .justify-content#{$infix}-start   { justify-content: flex-start !important; }
+    .justify-content#{$infix}-end     { justify-content: flex-end !important; }
+    .justify-content#{$infix}-center  { justify-content: center !important; }
+    .justify-content#{$infix}-between { justify-content: space-between !important; }
+    .justify-content#{$infix}-around  { justify-content: space-around !important; }
+
+    .align-items#{$infix}-start    { align-items: flex-start !important; }
+    .align-items#{$infix}-end      { align-items: flex-end !important; }
+    .align-items#{$infix}-center   { align-items: center !important; }
+    .align-items#{$infix}-baseline { align-items: baseline !important; }
+    .align-items#{$infix}-stretch  { align-items: stretch !important; }
+
+    .align-content#{$infix}-start   { align-content: flex-start !important; }
+    .align-content#{$infix}-end     { align-content: flex-end !important; }
+    .align-content#{$infix}-center  { align-content: center !important; }
+    .align-content#{$infix}-between { align-content: space-between !important; }
+    .align-content#{$infix}-around  { align-content: space-around !important; }
+    .align-content#{$infix}-stretch { align-content: stretch !important; }
+
+    .align-self#{$infix}-auto     { align-self: auto !important; }
+    .align-self#{$infix}-start    { align-self: flex-start !important; }
+    .align-self#{$infix}-end      { align-self: flex-end !important; }
+    .align-self#{$infix}-center   { align-self: center !important; }
+    .align-self#{$infix}-baseline { align-self: baseline !important; }
+    .align-self#{$infix}-stretch  { align-self: stretch !important; }
+  }
+}
diff --git a/webapp/src/styles.scss b/webapp/src/styles.scss
index 90d4ee00..df64f3c4 100644
--- a/webapp/src/styles.scss
+++ b/webapp/src/styles.scss
@@ -1 +1,19 @@
 /* You can add global styles to this file, and also import other style files */
+
+@import '~scss/bootstrap-grid.scss';
+
+html,
+body {
+  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+  margin: 0;
+  padding: 0;
+  height: 100%;
+}
+
+h1, h2, h3 {
+  font-size: 2em;
+  font-weight: 700;
+  padding-left: 0.5em;
+  margin: 0;
+  flex: 1;
+}
\ No newline at end of file
diff --git a/webapp/src/theme.scss b/webapp/src/theme.scss
new file mode 100644
index 00000000..9feaf1a1
--- /dev/null
+++ b/webapp/src/theme.scss
@@ -0,0 +1,17 @@
+// Charger les outils Material Design
+@import '~@angular/material/theming';
+
+// Charger les mixins de theming des composants
+@import 'theming/all-theme';
+
+// Charger le thème principal
+@import 'theming/grandlyon/grandlyon';
+
+// Le mixin `mat-core()` ne doit être appélé qu'une seule fois puisqu'il génère
+// des styles CSS.
+// TODO: passer en argument la configuration typographique.
+@include mat-core();
+
+@include angular-material-theme($gl-theme);
+@include app-theme($gl-theme);
+
diff --git a/webapp/src/theming/_all-theme.scss b/webapp/src/theming/_all-theme.scss
new file mode 100644
index 00000000..50310d03
--- /dev/null
+++ b/webapp/src/theming/_all-theme.scss
@@ -0,0 +1,29 @@
+@mixin app-theme($theme) {
+  // @include app-shared-theme($theme);
+  // @include app-components-theme($theme);
+}
+
+// Theming des éléments transverses
+
+// @import "shared/form-theme";
+// @import "shared/loader-theme";
+// @import "shared/table-theme";
+
+// @mixin app-shared-theme($theme) {
+//   @include app-shared-form-theme($theme);
+//   @include app-shared-loader-theme($theme);
+//   @include app-shared-table-theme($theme);
+// }
+
+// // Theming des composants Angular
+
+// @import "~app/app-theme.component";
+// @import "~app/components/menu/menu-theme.component";
+// @import "~app/components/federal/welcome/welcome-theme.component";
+
+// @mixin app-components-theme($theme) {
+//   @include app-app-theme($theme);
+//   @include app-menu-theme($theme);
+//   @include app-welcome-theme($theme);
+// }
+
diff --git a/webapp/src/theming/_theming.scss b/webapp/src/theming/_theming.scss
new file mode 100644
index 00000000..b09f09c5
--- /dev/null
+++ b/webapp/src/theming/_theming.scss
@@ -0,0 +1,11 @@
+@import "~sass-recursive-map-merge/recursive-map-merge";
+
+@function app-override-theme($mat-theme, $foreground: null, $background: null) {
+  $theme: (
+    foreground: $foreground,
+    background: $background,
+  );
+
+  @return recursive-map-merge($mat-theme, $theme);
+}
+
diff --git a/webapp/src/theming/grandlyon/config/_palette.scss b/webapp/src/theming/grandlyon/config/_palette.scss
new file mode 100644
index 00000000..eb2416e0
--- /dev/null
+++ b/webapp/src/theming/grandlyon/config/_palette.scss
@@ -0,0 +1,89 @@
+// Color palettes for Grand Lyon, la Métropole
+//
+// Generator:
+// http://mcg.mbitson.com/#!?vermilion=%23e10000&woodsmoke=%23050707&themename=grandlyon
+
+$gl-dark-primary-text: black;
+$gl-light-primary-text: white;
+
+$gl-vermilion: (
+    50:   #ffebeb,
+    100:  #ff9f9f,
+    200:  #ff6767,
+    300:  #ff1f1f,
+    400:  #ff0101,
+    500:  #e10000,
+    600:  #c20000,
+    700:  #a40000,
+    800:  #850000,
+    900:  #670000,
+    A100: #ffe1e1,
+    A200: #ff7b7b,
+    A400: #ff1515,
+    A700: #fa0000,
+    contrast: (
+        50:   $gl-dark-primary-text,
+        100:  $gl-dark-primary-text,
+        200:  $gl-dark-primary-text,
+        300:  $gl-light-primary-text,
+        400:  $gl-light-primary-text,
+        500:  $gl-light-primary-text,
+        600:  $gl-light-primary-text,
+        700:  $gl-light-primary-text,
+        800:  $gl-light-primary-text,
+        900:  $gl-light-primary-text,
+        A100: $gl-dark-primary-text,
+        A200: $gl-dark-primary-text,
+        A400: $gl-light-primary-text,
+        A700: $gl-light-primary-text,
+    )
+);
+
+$gl-woodsmoke: (
+    50:   #e1e1e1,
+    100:  #b4b5b5,
+    200:  #828383,
+    300:  #505151,
+    400:  #2b2c2c,
+    500:  #050707,
+    600:  #040606,
+    700:  #040505,
+    800:  #030404,
+    900:  #010202,
+    A100: #a6a6a6,
+    A200: #8c8c8c,
+    A400: #737373,
+    A700: #666666,
+    contrast: (
+        50:   $gl-dark-primary-text,
+        100:  $gl-dark-primary-text,
+        200:  $gl-dark-primary-text,
+        300:  $gl-light-primary-text,
+        400:  $gl-light-primary-text,
+        500:  $gl-light-primary-text,
+        600:  $gl-light-primary-text,
+        700:  $gl-light-primary-text,
+        800:  $gl-light-primary-text,
+        900:  $gl-light-primary-text,
+        A100: $gl-dark-primary-text,
+        A200: $gl-dark-primary-text,
+        A400: $gl-light-primary-text,
+        A700: $gl-light-primary-text,
+    )
+);
+
+// Palette des couleurs d'arrière plan spécifique au thème clair.
+$gl-light-theme-background: (
+  // spécifiques
+  actived-navigation: #f0f0f0,
+  form: rgba(220, 220, 220, 0.4),
+  table-header: #d3d3d3,
+);
+
+// Palette des couleurs de premier plan spécifique au thème clair.
+$gl-light-theme-foreground: (
+  // spécifiques
+  valid:   #4caf50,
+  invalid: #d32f2f,
+);
+
diff --git a/webapp/src/theming/grandlyon/config/_variables.scss b/webapp/src/theming/grandlyon/config/_variables.scss
new file mode 100644
index 00000000..2ac14af9
--- /dev/null
+++ b/webapp/src/theming/grandlyon/config/_variables.scss
@@ -0,0 +1,9 @@
+@import "palette";
+
+// Palettes
+// --------
+
+$gl-theme-primary: mat-palette($gl-vermilion);
+$gl-theme-accent:  mat-palette($gl-woodsmoke);
+$gl-theme-warn:    mat-palette($gl-woodsmoke);
+
diff --git a/webapp/src/theming/grandlyon/grandlyon.scss b/webapp/src/theming/grandlyon/grandlyon.scss
new file mode 100644
index 00000000..73837988
--- /dev/null
+++ b/webapp/src/theming/grandlyon/grandlyon.scss
@@ -0,0 +1,10 @@
+@import '../theming';
+
+@import "config/variables";
+
+$gl-theme: app-override-theme(
+  mat-light-theme($gl-theme-primary, $gl-theme-accent, $gl-theme-warn),
+  $foreground: $gl-light-theme-foreground,
+  $background: $gl-light-theme-background
+);
+
-- 
GitLab