diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..0bf33b6c731afb0b54ed07a64b781d02decd8560
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,25 @@
+image: maven:3.8.3-openjdk-17
+
+variables:
+  MAVEN_OPTS: "-Djava.awt.headless=true -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository"
+  MAVEN_CLI_OPTS: "--batch-mode -P headless -DskipUiTests=true"
+
+stages:
+  - test
+  - build
+
+cache:
+  paths:
+    - .m2/repository
+  key: "$CI_BUILD_REF_NAME"
+
+test:
+  stage: test
+  script:
+    - "cd todolist; mvn clean test $MAVEN_CLI_OPTS"
+
+build:
+  stage: build
+  when: manual
+  script:
+    - "cd todolist; mvn clean install $MAVEN_CLI_OPTS"
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000000000000000000000000000000000000..09ff540fc762c163a5ad333b7d23e299c2a080a2
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,38 @@
+{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "type": "java",
+            "name": "Launch Current File",
+            "request": "launch",
+            "mainClass": "${file}"
+        },
+        {
+            "type": "java",
+            "name": "Launch TodoApp",
+            "request": "launch",
+            "mainClass": "todolist.ui/todolist.ui.TodoApp",
+            "vmArgs": "--add-opens todolist.ui/todolist.ui=ALL-UNNAMED --add-exports javafx.graphics/com.sun.javafx.application=ALL-UNNAMED",
+            "projectName": "fxui"
+        },
+        {
+            "type": "java",
+            "name": "Launch TodoDocumentApp",
+            "request": "launch",
+            "mainClass": "todolist.ui/todolist.ui.TodoDocumentApp",
+            "vmArgs": "--add-opens todolist.ui/todolist.ui=ALL-UNNAMED --add-exports javafx.graphics/com.sun.javafx.application=ALL-UNNAMED",
+            "projectName": "fxui"
+        },
+        {
+            "type": "java",
+            "name": "Launch TodoRemoteApp",
+            "request": "launch",
+            "mainClass": "todolist.ui/todolist.ui.TodoRemoteApp",
+            "vmArgs": "--add-opens todolist.ui/todolist.ui=ALL-UNNAMED --add-exports javafx.graphics/com.sun.javafx.application=ALL-UNNAMED",
+            "projectName": "fxui"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 30a607b96186ef70bd90a43ef36cf617e28e3701..277f17807a4bd7450dfdd00d8fa23ba2b5a6079e 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,3 +1,18 @@
 {
     "java.configuration.updateBuildConfiguration": "automatic",
+    "java.format.settings.url": "todolist/config/checkstyle/eclipse-java-google-style.xml",
+    "[java]": {
+        "editor.tabSize": 2
+    },
+    "java.test.config": [
+        {
+            "name": "coreTesting",
+            "vmArgs": []
+        },
+        {
+            "name": "fxuiTesting",
+            "vmArgs": ["--add-opens", "todolist.ui/todolist.ui=ALL-UNNAMED", "--add-exports", "javafx.graphics/com.sun.javafx.application=ALL-UNNAMED"]
+        }
+    ],
+    "java.test.defaultConfig": "fxuiTesting"
 }
\ No newline at end of file
diff --git a/todolist/architecture.puml b/todolist/architecture.puml
index f03073addddac96aaffa145839363adc8f16d6ea..08ac67eba946db82fb4747371b5839b2c78dc28e 100644
--- a/todolist/architecture.puml
+++ b/todolist/architecture.puml
@@ -5,6 +5,7 @@ component core {
 	package todolist.json
 }
 
+todolist.core ..> todolist.json
 component jackson {
 }
 
@@ -15,9 +16,10 @@ component fxutil {
 
 component fxui {
 	package todolist.ui
+	package todolist.ui.util
 }
 
-
+todolist.ui ..> todolist.ui.util
 todolist.ui ..> todolist.core
 todolist.ui ..> todolist.json
 
@@ -55,16 +57,4 @@ component grizzly2 {
 rest ..> jersey
 rest ..> grizzly2
 
-component "springboot/restserver" as springboot.restserver {
-	package todolist.springboot.restserver
-}
-
-todolist.springboot.restserver ..> todolist.core
-todolist.springboot.restserver ..> todolist.json
-
-component "spring boot" as springboot {
-}
-
-springboot.restserver ..> springboot
-
 @enduml
\ No newline at end of file
diff --git a/todolist/fxui/pom.xml b/todolist/fxui/pom.xml
index 08757f61a60ef323bf37143418c5ae075889f93d..b627ecd6a47641b4e13ac408c5966a278119a73f 100644
--- a/todolist/fxui/pom.xml
+++ b/todolist/fxui/pom.xml
@@ -11,10 +11,6 @@
 
     <artifactId>fxui</artifactId>
 
-    <properties>
-        <skipUiTests>false</skipUiTests>
-    </properties>
-
     <dependencies>
         <dependency>
             <groupId>it1901.todolist</groupId>
@@ -167,6 +163,22 @@
                 </plugins>
             </build>
         </profile>
+
+        <profile>
+            <id>headless</id>
+            <properties>
+                <headlessTests>true</headlessTests>
+            </properties>
+            <dependencies>
+                <!-- https://mvnrepository.com/artifact/org.testfx/openjfx-monocle -->
+                <dependency>
+                    <groupId>org.testfx</groupId>
+                    <artifactId>openjfx-monocle</artifactId>
+                    <version>jdk-12.0.1+2</version>
+                    <scope>test</scope>
+                </dependency>
+            </dependencies>
+        </profile>
     </profiles>
 
     <build>
@@ -179,17 +191,50 @@
                 <artifactId>maven-surefire-plugin</artifactId>
                 <configuration>
                     <skipTests>${skipUiTests}</skipTests>
+                    <systemPropertyVariables>
+                        <headless>${headlessTests}</headless>
+                    </systemPropertyVariables>
                     <argLine>
-                    @{jaCoCoArgLine} --add-opens todolist.ui/todolist.ui=ALL-UNNAMED --add-exports javafx.graphics/com.sun.javafx.application=ALL-UNNAMED
+                    @{jaCoCoArgLine}
+                    --add-opens todolist.ui/todolist.ui=ALL-UNNAMED --add-exports javafx.graphics/com.sun.javafx.application=ALL-UNNAMED
+                    --add-opens javafx.graphics/com.sun.glass.ui=ALL-UNNAMED --add-exports javafx.graphics/com.sun.glass.ui=ALL-UNNAMED
+                    --add-exports javafx.graphics/com.sun.glass.ui.delegate=ALL-UNNAMED
                     </argLine>
                 </configuration>
             </plugin>
+
             <plugin>
                 <groupId>org.openjfx</groupId>
                 <artifactId>javafx-maven-plugin</artifactId>
-                <version>0.0.6</version>
+                <version>0.0.8</version>
                 <configuration>
                     <mainClass>todolist.ui/todolist.ui.TodoApp</mainClass>
+                    <!-- rest of options are for jlink -->
+                    <compress>2</compress>
+                    <noHeaderFiles>true</noHeaderFiles>
+                    <stripDebug>true</stripDebug>
+                    <noManPages>true</noManPages>
+                    <launcher>todolistfx</launcher>
+                    <jlinkImageName>todolistfx</jlinkImageName>
+                    <jlinkZipName>todolistfx</jlinkZipName>
+                </configuration>
+            </plugin>
+
+            <!-- config above and below taken from https://dev.to/cherrychain/javafx-jlink-and-jpackage-h9 -->
+            <plugin>
+                <groupId>org.panteleyev</groupId>
+                <artifactId>jpackage-maven-plugin</artifactId>
+                <version>1.4.0</version>
+                <configuration>
+                    <name>TodoListFX</name>
+                    <appVersion>1.0.0</appVersion>
+                    <vendor>it1901</vendor>
+                    <destination>target/dist</destination>
+                    <module>todolist.ui/todolist.ui.TodoApp</module>
+                    <runtimeImage>target/todolistfx</runtimeImage>
+                    <javaOptions>
+                        <option>-Dfile.encoding=UTF-8</option>
+                    </javaOptions>
                 </configuration>
             </plugin>
 
diff --git a/todolist/fxui/src/main/java/module-info.java b/todolist/fxui/src/main/java/module-info.java
index b1707f4ef6ea8238bc99c8cac4dfd7921e43e8a6..a17a48adae0e4028a0657498a7321d3a2984427a 100644
--- a/todolist/fxui/src/main/java/module-info.java
+++ b/todolist/fxui/src/main/java/module-info.java
@@ -7,6 +7,9 @@ module todolist.ui {
     requires javafx.controls;
     requires javafx.fxml;
 
+    requires de.jensd.fx.glyphs.commons;
+    requires de.jensd.fx.glyphs.fontawesome;
+
     requires todolist.core;
     requires fxutil;
 
diff --git a/todolist/fxui/src/main/java/todolist/ui/TodoApp.java b/todolist/fxui/src/main/java/todolist/ui/TodoApp.java
index ff4f8268f17c25b391decfe102b439ad1c8b8c07..a9e4bcfae7ca511f8cd871f5ea65678244e22c37 100644
--- a/todolist/fxui/src/main/java/todolist/ui/TodoApp.java
+++ b/todolist/fxui/src/main/java/todolist/ui/TodoApp.java
@@ -11,6 +11,19 @@ import javafx.stage.Stage;
  */
 public class TodoApp extends Application {
 
+  /**
+   * Helper method used by tests needing to run headless.
+   */
+  public static void supportHeadless() {
+    if (Boolean.getBoolean("headless")) {
+      System.setProperty("testfx.robot", "glass");
+      System.setProperty("testfx.headless", "true");
+      System.setProperty("prism.order", "sw");
+      System.setProperty("prism.text", "t2k");
+      System.setProperty("java.awt.headless", "true");
+    }
+  }
+
   @Override
   public void start(Stage stage) throws Exception {
     Parent parent = FXMLLoader.load(getClass().getResource("TodoApp.fxml"));
diff --git a/todolist/fxui/src/test/java/todolist/ui/TodoAppTest.java b/todolist/fxui/src/test/java/todolist/ui/TodoAppTest.java
index d75456482051a80369537f536c9231f7d2f50db1..1e6b1ee06a062199d905ab880f92e243f661b4ba 100644
--- a/todolist/fxui/src/test/java/todolist/ui/TodoAppTest.java
+++ b/todolist/fxui/src/test/java/todolist/ui/TodoAppTest.java
@@ -10,12 +10,19 @@ import static org.junit.jupiter.api.Assertions.fail;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.Reader;
+
+import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.testfx.framework.junit5.ApplicationTest;
 
 public class TodoAppTest extends ApplicationTest {
 
+  @BeforeAll
+  public static void setupHeadless() {
+    TodoApp.supportHeadless();
+  }
+
   private TodoModelController controller;
 
   @Override
@@ -26,7 +33,7 @@ public class TodoAppTest extends ApplicationTest {
     stage.setScene(new Scene(root));
     stage.show();
   }
-  
+
   private TodoPersistence todoPersistence = new TodoPersistence();
 
   @BeforeEach
diff --git a/todolist/fxui/src/test/java/todolist/ui/TodoListControllerTest.java b/todolist/fxui/src/test/java/todolist/ui/TodoListControllerTest.java
index 3c6c1c7df1e9a468c28ddc7bc4f8f70eccc36b48..2de3cf0ef3d29d49770ff4e978f4041a6299f753 100644
--- a/todolist/fxui/src/test/java/todolist/ui/TodoListControllerTest.java
+++ b/todolist/fxui/src/test/java/todolist/ui/TodoListControllerTest.java
@@ -16,10 +16,11 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable;
 import org.testfx.framework.junit5.ApplicationTest;
 import org.testfx.util.WaitForAsyncUtils;
 import todolist.core.AbstractTodoList;
@@ -31,6 +32,11 @@ import todolist.json.TodoPersistence;
 
 public class TodoListControllerTest extends ApplicationTest {
 
+  @BeforeAll
+  public static void setupHeadless() {
+    TodoApp.supportHeadless();
+  }
+
   private TodoListController controller;
 
   @Override
@@ -123,7 +129,6 @@ public class TodoListControllerTest extends ApplicationTest {
   /** Test - virker ikke i gitpod */
   @Test
   @DisplayName("Test dragging a ToDo list item")
-  @DisabledIfEnvironmentVariable(named = "GITPOD_WORKSPACE_ID", matches = ".*")
   public void testDragTodoItem() {
     Predicate<TodoItemListCell> draggableCell = cell -> cell.lookup(".label") != null;
     // drag the first item in the list view, which is the second item in the model
diff --git a/todolist/integrationtests/pom.xml b/todolist/integrationtests/pom.xml
index a2c110631d959acfa3fd365fee91d468241a34cd..0c2de4b8a2b51220c323af049edb3262b65085a8 100644
--- a/todolist/integrationtests/pom.xml
+++ b/todolist/integrationtests/pom.xml
@@ -75,6 +75,24 @@
         </dependency>
     </dependencies>
 
+    <profiles>
+        <profile>
+            <id>headless</id>
+            <properties>
+                <headlessTests>true</headlessTests>
+            </properties>
+            <dependencies>
+                <!-- https://mvnrepository.com/artifact/org.testfx/openjfx-monocle -->
+                <dependency>
+                    <groupId>org.testfx</groupId>
+                    <artifactId>openjfx-monocle</artifactId>
+                    <version>jdk-12.0.1+2</version>
+                    <scope>test</scope>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+
     <build>
         <plugins>
             <plugin>
@@ -147,8 +165,10 @@
                             <goal>verify</goal>
                         </goals>
                         <configuration>
+                            <skipTests>${skipUiTests}</skipTests>
                             <!-- System.getProperty("todo.port") gir riktig port -->
                             <systemPropertyVariables>
+                                <headless>${headlessTests}</headless>
                                 <todo.port>${jetty.port}</todo.port>
                             </systemPropertyVariables>
                         </configuration>
diff --git a/todolist/integrationtests/src/test/java/todolist/ui/TodoAppIT.java b/todolist/integrationtests/src/test/java/todolist/ui/TodoAppIT.java
index ca4e919365106c621f2b3ecf9069904d6462f7d9..d8c37a10b977002b8e15471a077aa18119f4bafc 100644
--- a/todolist/integrationtests/src/test/java/todolist/ui/TodoAppIT.java
+++ b/todolist/integrationtests/src/test/java/todolist/ui/TodoAppIT.java
@@ -11,12 +11,18 @@ import java.io.InputStreamReader;
 import java.io.Reader;
 import java.net.URI;
 import java.net.URISyntaxException;
+import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.testfx.framework.junit5.ApplicationTest;
 
 public class TodoAppIT extends ApplicationTest {
 
+  @BeforeAll
+  public static void setupHeadless() {
+    TodoApp.supportHeadless();
+  }
+
   private TodoModelController controller;
 
   @Override
diff --git a/todolist/pom.xml b/todolist/pom.xml
index f5d53ffc35f41e18e1c531a8c30dafda65d41d43..4b2170dc2bb54787e16e7edfd11f8dfe498caef4 100644
--- a/todolist/pom.xml
+++ b/todolist/pom.xml
@@ -12,6 +12,8 @@
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <jupiter.version>5.8.1</jupiter.version>
         <jackson.version>2.12.5</jackson.version>
+        <skipUiTests>false</skipUiTests>
+        <headlessTests>false</headlessTests>
     </properties>
 
     <dependencyManagement>