diff --git a/.github/workflows/autodeploy.yml b/.github/workflows/autodeploy.yml
index a950a15..8d1bba2 100644
--- a/.github/workflows/autodeploy.yml
+++ b/.github/workflows/autodeploy.yml
@@ -1,35 +1,36 @@
 name: deploy-front-end
 
 on:
-    push:
-        branches: [main]
+  push:
+    branches: [main]
 
-    # Allows you to run this workflow manually from the Actions tab
-    workflow_dispatch:
+  # Allows you to run this workflow manually from the Actions tab
+  workflow_dispatch:
 
 # A workflow run is made up of one or more jobs that can run sequentially or in parallel
 jobs:
-    publish:
-        runs-on: ubuntu-latest
-        steps:
-            # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
-            - name: Check out repo
-              uses: actions/checkout@v2
-            # Node is required for npm
-            - name: Set up Node
-              uses: actions/setup-node@v2
-              with:
-                  node-version: "16.15.1"
-            # Install and build Docusaurus website
-            - name: Build Resume
-              run: |
-                  npm install
-                  npm run build
-            - name: Deploy to GitHub Pages
-              if: success()
-              uses: crazy-max/ghaction-github-pages@v2
-              with:
-                  target_branch: gh-pages
-                  build_dir: public
-              env:
-                  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+  publish:
+    runs-on: ubuntu-latest
+    steps:
+      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
+      - name: Check out repo
+        uses: actions/checkout@v2
+
+      - name: Set up Bun
+        uses: oven-sh/setup-bun@v1
+        with:
+          bun-version: "1.1.4"
+
+      - name: Build Resume
+        run: |
+          bun install
+          bun run build
+
+      - name: Deploy to GitHub Pages
+        if: success()
+        uses: crazy-max/ghaction-github-pages@v2
+        with:
+          target_branch: gh-pages
+          build_dir: dist
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
index da93220..a547bf3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,24 @@
-/node_modules/
-/public/build/
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
 
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
 .DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/Bornais_Jeremie_Resume.pdf b/Bornais_Jeremie_Resume.pdf
deleted file mode 100644
index d7ad243..0000000
Binary files a/Bornais_Jeremie_Resume.pdf and /dev/null differ
diff --git a/LICENSE b/LICENSE
index ca7b488..786bffc 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,21 +1,22 @@
-MIT License
-
-Copyright (c) 2022 Jeremie Bornais
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+MIT License
+
+Copyright (c) 2024 Isaac Kilbourne
+Copyright (c) 2022 Jeremie Bornais
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index 5e33a49..96522d5 100644
--- a/README.md
+++ b/README.md
@@ -1,34 +1,20 @@
-# Svelte-Resume
+# svelte-resume
+
+> **Svelte**: from French *svelte*, "slim, slender"
+> 
+> **Resume**: from French *résumé*, "a summary, summing up, recapitulation"
 
 Resume for Jeremie Bornais, created in Svelte.
 
 ## Get started
 
-Install the dependencies...
-
-```bash
-cd resume
-npm install
-```
+This project uses [Bun](bun.sh), so you will need to [install Bun](https://bun.sh/) if you have not already.
 
-...then start [Rollup](https://rollupjs.org):
+Then just two commands:
 
-```bash
-npm run dev
-```
-
-Navigate to [localhost:8080](http://localhost:8080).
+1. `bun install` to install dependencies
+2. `bun run dev` to run a development server
 
 ## This is a Website, not a Resume
 
 Yes, this is a website. BUT, it is also a resume. To turn this into an actual file, simply 'print' the webpage, and save to PDF!
-
-## Building and running in production mode
-
-To create an optimised version of the app:
-
-```bash
-npm run build
-```
-
-You can run the newly built app with `npm run start`. This uses [sirv](https://github.com/lukeed/sirv).
diff --git a/bun.lockb b/bun.lockb
new file mode 100644
index 0000000..af8bf3f
Binary files /dev/null and b/bun.lockb differ
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..5201a17
--- /dev/null
+++ b/index.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width,initial-scale=1" />
+
+    <title>Kilbourne_Isaac_Resume</title>
+    <script
+      src="https://kit.fontawesome.com/22fc08ce26.js"
+      crossorigin="anonymous"
+    ></script>
+  </head>
+
+  <body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.ts"></script>
+  </body>
+</html>
diff --git a/package.json b/package.json
index fd88a32..a9d712d 100644
--- a/package.json
+++ b/package.json
@@ -1,23 +1,21 @@
 {
-  "name": "resume",
-  "version": "1.0.0",
-  "private": true,
-  "scripts": {
-    "build": "rollup -c",
-    "dev": "rollup -c -w",
-    "start": "sirv public --no-clear"
-  },
-  "devDependencies": {
-    "@rollup/plugin-commonjs": "^17.0.0",
-    "@rollup/plugin-node-resolve": "^11.0.0",
-    "rollup": "^2.3.4",
-    "rollup-plugin-css-only": "^3.1.0",
-    "rollup-plugin-livereload": "^2.0.0",
-    "rollup-plugin-svelte": "^7.0.0",
-    "rollup-plugin-terser": "^7.0.0",
-    "svelte": "^3.0.0"
-  },
-  "dependencies": {
-    "sirv-cli": "^2.0.0"
-  }
+    "name": "resume",
+    "private": true,
+    "version": "0.0.0",
+    "type": "module",
+    "scripts": {
+        "dev": "vite",
+        "build": "vite build",
+        "preview": "vite preview",
+        "check": "svelte-check --tsconfig ./tsconfig.json"
+    },
+    "devDependencies": {
+        "@sveltejs/vite-plugin-svelte": "^3.0.2",
+        "@tsconfig/svelte": "^5.0.2",
+        "svelte": "^4.2.12",
+        "svelte-check": "^3.6.7",
+        "tslib": "^2.6.2",
+        "typescript": "^5.2.2",
+        "vite": "^5.2.0"
+    }
 }
diff --git a/public/education.json b/public/education.json
deleted file mode 100644
index ae5d5f2..0000000
--- a/public/education.json
+++ /dev/null
@@ -1,25 +0,0 @@
-[
-  {
-    "school": "University of Windsor",
-    "degree": "Bachelors of Computer Science (Honours)",
-    "duration": "September 2019 - October 2023",
-    "points": [
-      {
-        "text": "<b>97.40%</b> Major Average, <b>97.25%</b> Cumulative Average"
-      },
-      {
-        "text": "Recipient of the President's Scholarship, Eleanor Catherine Wallace Memorial Scholarship, and Foresters Scholarship"
-      }
-    ]
-  },
-  {
-    "school": "Business Career College",
-    "degree": "Life License Qualification Program (LLQP)",
-    "duration": "August 2023 - October 2023",
-    "points": [
-      {
-        "text": "Completed the LLQP program to obtain my Ontario Life Insurance and Accident & Sickness Insurance licenses"
-      }
-    ]
-  }
-]
\ No newline at end of file
diff --git a/public/experience.json b/public/experience.json
deleted file mode 100644
index 86031f0..0000000
--- a/public/experience.json
+++ /dev/null
@@ -1,72 +0,0 @@
-[
-  {
-    "title": "Software Engineer Intern",
-    "company": "Publicis Sapient",
-    "time": "June 2022 - August 2022",
-    "location": "Toronto, ON",
-    "points": [
-      {
-        "text": "Created and deployed a <b>GraphQL</b> API using <b>Java, Spring Boot, and PostgreSQL</b> for a personal budgeting mobile app."
-      },
-      {
-        "text": "Used <b>GitHub Actions</b> to set up automated <b>CI/CD</b> workflows."
-      }
-    ]
-  },
-  {
-    "title": "Software Developer Intern",
-    "company": "Assent Inc.",
-    "time": "January 2022 - April 2022",
-    "location": "Ottawa, ON",
-    "points": [
-      {
-        "text": "Used <b>Angular and SCSS</b> to create and update front-end components, adhering to strict styling standards."
-      },
-      {
-        "text": "Used <b>C# and .NET Core</b> to create API endpoints, unit tests, component tests, and integration tests."
-      }
-    ]
-  },
-  {
-    "title": "Software Developer Intern",
-    "company": "Green Shield Canada",
-    "time": "May 2021 - August 2021",
-    "location": "Windsor, ON",
-    "points": [
-      {
-        "text": "Used <b>Oracle PL/SQL</b> to create Data Reports and Extracts"
-      }
-    ]
-  },
-  {
-    "title": "Research Software Developer",
-    "company": "University of Windsor",
-    "time": "June 2020 - April 2023",
-    "location": "Windsor, ON",
-    "points": [
-      {
-        "text": "Developed a <b>web application</b> for the analysis and presentation of experimental SAS data"
-      },
-      {
-        "text": "Achieved <b>100X faster compute time</b> of select elements"
-      },
-      {
-        "text": "Using <b>Flask, Bootstrap, NumPy, SciPy, and Matplotlib</b>"
-      }
-    ]
-  },
-  {
-    "title": "Teaching Assistant",
-    "company": "University of Windsor",
-    "time": "September 2020 - June 2023",
-    "location": "Windsor, ON",
-    "points": [
-      {
-        "text": "Responsible for lab instruction, marking, and holding office hours for <b>4 core undergraduate courses</b>"
-      },
-      {
-        "text": "<b>Built and deployed a web application</b> that grades assignments</b>"
-      }
-    ]
-  }
-]
diff --git a/public/extracurricular.json b/public/extracurricular.json
deleted file mode 100644
index a5a91ad..0000000
--- a/public/extracurricular.json
+++ /dev/null
@@ -1,14 +0,0 @@
-[
-  {
-    "title": "President | Computer Science Society",
-    "description": "Led a team of 20 volunteers to create applications, organize events, and represent over 900 undergraduate students (2021-2023)"
-  },
-  {
-    "title": "Co-Founder and Technical Lead | WinHacks",
-    "description": "MLH Hackathon (winhacks.ca) (2020-2022)"
-  },
-  {
-    "title": "Co-Founder and Technical Lead | BorderHacks",
-    "description": "MLH Hackathon (borderhacks.com) (2020-2021)"
-  }
-]
diff --git a/public/favicon.png b/public/favicon.png
deleted file mode 100644
index 7e6f5eb..0000000
Binary files a/public/favicon.png and /dev/null differ
diff --git a/public/global.css b/public/global.css
deleted file mode 100644
index 975bea6..0000000
--- a/public/global.css
+++ /dev/null
@@ -1,74 +0,0 @@
-html, body {
-	margin: 0;
-	padding: 0;
-	position: relative;
-}
-
-body {
-	color: #000;
-	margin: 0.25in 0.5in;
-	padding: 8px;
-	box-sizing: border-box;
-	font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
-}
-
-h1 {
-	margin: 0;
-	padding: 0;
-}
-
-hr {
-	border-top: solid 4px #000 !important;
-	margin: 4px;
-}
-
-a{
-	text-decoration: none;
-	color: #000;
-}
-
-h3.primary-heading {
-	font-size: 16px;
-	font-weight: 400;
-	margin: 5px 0px;
-}
-
-h4.secondary-heading {
-	font-size: 14px;
-	font-weight: 600;
-	margin: 5px 0px;
-}
-
-.icon-row{
-	display: flex;
-	justify-content: space-between;
-}
-
-.icon-row > span{
-	font-size: 12px;
-	padding-right: 20px;        
-}
-
-.point-text {
-	font-size: 12px;
-}
-
-.dotted-separator {
-	border:none;
-	border-top: 1px dashed #888;
-	margin: 10px 0px;
-}
-
-i {
-	margin-right: 3px;
-}
-
-ul {
-	padding-left: 0;
-	margin: 5px 0px;
-	list-style-type: none;
-}
-
-.tiny-adjust{
-	margin-top: 3px;
-}
\ No newline at end of file
diff --git a/public/header.json b/public/header.json
deleted file mode 100644
index 968a6f0..0000000
--- a/public/header.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-  "name": "Jeremie Bornais",
-  "email": "jeremiejbornais@gmail.com",
-  "phone": "(226) 350-2944",
-  "website": "jeremie.bornais.ca",
-  "github": "jere-mie",
-  "linkedin": "jeremie-bornais"
-}
diff --git a/public/index.html b/public/index.html
deleted file mode 100644
index b7931ba..0000000
--- a/public/index.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-	<meta charset='utf-8'>
-	<meta name='viewport' content='width=device-width,initial-scale=1'>
-
-	<title>Bornais_Jeremie_Resume</title>
-
-	<link rel='icon' type='image/png' href='https://f.m.bornais.ca/logo.png'>
-	<link rel='stylesheet' href='./global.css'>
-	<link rel='stylesheet' href='./build/bundle.css'>
-    <script src="https://kit.fontawesome.com/22fc08ce26.js" crossorigin="anonymous"></script>
-	<script defer src='./build/bundle.js'></script>
-</head>
-
-<body>
-</body>
-</html>
diff --git a/public/projects.json b/public/projects.json
deleted file mode 100644
index b791df2..0000000
--- a/public/projects.json
+++ /dev/null
@@ -1,47 +0,0 @@
-[
-  {
-    "title": "Automagic Websites",
-    "link": "https://github.com/jere-mie/automagic-websites",
-    "points": [
-      {
-        "text": "Full stack web application featuring <b>user authentication</b>"
-      },
-      {
-        "text": "Allows users to <b>create, host, and export</b> their own personal website"
-      },
-      {
-        "text": "Made with <b>Python, Flask, SQLAlchemy, Bootstrap</b>"
-      }
-    ]
-  },
-  {
-    "title": "UWindsor API",
-    "link": "https://github.com/jere-mie/uwindsor-api",
-    "points": [
-      {
-        "text": "<b>REST API</b> created to enable developers to create applications for the UWindsor community"
-      },
-      {
-        "text": "Serves course, staff and building data related to the University of Windsor"
-      },
-      {
-        "text": "Made with <b>Go, Gin, and SQLX</b>"
-      }
-    ]
-  },
-  {
-    "title": "Easy-MASM",
-    "link": "https://github.com/jere-mie/easy-masm",
-    "points": [
-      {
-        "text": "Automated system for assembling, linking, and executing <b>MASM32</b> programs"
-      },
-      {
-        "text": "Used by over <b>500 students</b>"
-      },
-      {
-        "text": "Made with <b>Docker, bash and batch</b>"
-      }
-    ]
-  }
-]
diff --git a/public/skills.json b/public/skills.json
deleted file mode 100644
index de30f2a..0000000
--- a/public/skills.json
+++ /dev/null
@@ -1,18 +0,0 @@
-[
-  {
-    "type": "PROGRAMMING",
-    "list": "Python, JavaScript, Go, Java, C#, HTML, CSS, SCSS, C++, SQL, C, Ruby"
-  },
-  {
-    "type": "FRAMEWORKS",
-    "list": "Flask, React Native, Svelte, Angular, React.js, Gin, .NET Core, Spring Boot, Bootstrap, Ruby on Rails"
-  },
-  {
-    "type": "TECHNOLOGIES",
-    "list": "Git, Linux, Docker, Caddy Server, GraphQL, REST API, OAuth2, CI/CD, GitHub Actions"
-  },
-  {
-    "type": "CLOUD",
-    "list": "Google Cloud Platform, Microsoft Azure, DigitalOcean, Heroku"
-  }
-]
diff --git a/public/text.json b/public/text.json
deleted file mode 100644
index eca14a5..0000000
--- a/public/text.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-    "text":"Bachelors of Computer Science (Honours), University of Windsor, September 2019 - October 2023, 97.25% GPA"
-}
\ No newline at end of file
diff --git a/rollup.config.js b/rollup.config.js
deleted file mode 100644
index e8965ec..0000000
--- a/rollup.config.js
+++ /dev/null
@@ -1,76 +0,0 @@
-import svelte from 'rollup-plugin-svelte';
-import commonjs from '@rollup/plugin-commonjs';
-import resolve from '@rollup/plugin-node-resolve';
-import livereload from 'rollup-plugin-livereload';
-import { terser } from 'rollup-plugin-terser';
-import css from 'rollup-plugin-css-only';
-
-const production = !process.env.ROLLUP_WATCH;
-
-function serve() {
-	let server;
-
-	function toExit() {
-		if (server) server.kill(0);
-	}
-
-	return {
-		writeBundle() {
-			if (server) return;
-			server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
-				stdio: ['ignore', 'inherit', 'inherit'],
-				shell: true
-			});
-
-			process.on('SIGTERM', toExit);
-			process.on('exit', toExit);
-		}
-	};
-}
-
-export default {
-	input: 'src/main.js',
-	output: {
-		sourcemap: true,
-		format: 'iife',
-		name: 'app',
-		file: 'public/build/bundle.js'
-	},
-	plugins: [
-		svelte({
-			compilerOptions: {
-				// enable run-time checks when not in production
-				dev: !production
-			}
-		}),
-		// we'll extract any component CSS out into
-		// a separate file - better for performance
-		css({ output: 'bundle.css' }),
-
-		// If you have external dependencies installed from
-		// npm, you'll most likely need these plugins. In
-		// some cases you'll need additional configuration -
-		// consult the documentation for details:
-		// https://github.com/rollup/plugins/tree/master/packages/commonjs
-		resolve({
-			browser: true,
-			dedupe: ['svelte']
-		}),
-		commonjs(),
-
-		// In dev mode, call `npm run start` once
-		// the bundle has been generated
-		!production && serve(),
-
-		// Watch the `public` directory and refresh the
-		// browser on changes when not in production
-		!production && livereload('public'),
-
-		// If we're building for production (npm run build
-		// instead of npm run dev), minify
-		production && terser()
-	],
-	watch: {
-		clearScreen: false
-	}
-};
diff --git a/scripts/setupTypeScript.js b/scripts/setupTypeScript.js
deleted file mode 100644
index 133658a..0000000
--- a/scripts/setupTypeScript.js
+++ /dev/null
@@ -1,121 +0,0 @@
-// @ts-check
-
-/** This script modifies the project to support TS code in .svelte files like:
-
-  <script lang="ts">
-  	export let name: string;
-  </script>
- 
-  As well as validating the code for CI.
-  */
-
-/**  To work on this script:
-  rm -rf test-template template && git clone sveltejs/template test-template && node scripts/setupTypeScript.js test-template
-*/
-
-const fs = require("fs")
-const path = require("path")
-const { argv } = require("process")
-
-const projectRoot = argv[2] || path.join(__dirname, "..")
-
-// Add deps to pkg.json
-const packageJSON = JSON.parse(fs.readFileSync(path.join(projectRoot, "package.json"), "utf8"))
-packageJSON.devDependencies = Object.assign(packageJSON.devDependencies, {
-  "svelte-check": "^2.0.0",
-  "svelte-preprocess": "^4.0.0",
-  "@rollup/plugin-typescript": "^8.0.0",
-  "typescript": "^4.0.0",
-  "tslib": "^2.0.0",
-  "@tsconfig/svelte": "^2.0.0"
-})
-
-// Add script for checking
-packageJSON.scripts = Object.assign(packageJSON.scripts, {
-  "check": "svelte-check --tsconfig ./tsconfig.json"
-})
-
-// Write the package JSON
-fs.writeFileSync(path.join(projectRoot, "package.json"), JSON.stringify(packageJSON, null, "  "))
-
-// mv src/main.js to main.ts - note, we need to edit rollup.config.js for this too
-const beforeMainJSPath = path.join(projectRoot, "src", "main.js")
-const afterMainTSPath = path.join(projectRoot, "src", "main.ts")
-fs.renameSync(beforeMainJSPath, afterMainTSPath)
-
-// Switch the app.svelte file to use TS
-const appSveltePath = path.join(projectRoot, "src", "App.svelte")
-let appFile = fs.readFileSync(appSveltePath, "utf8")
-appFile = appFile.replace("<script>", '<script lang="ts">')
-appFile = appFile.replace("export let name;", 'export let name: string;')
-fs.writeFileSync(appSveltePath, appFile)
-
-// Edit rollup config
-const rollupConfigPath = path.join(projectRoot, "rollup.config.js")
-let rollupConfig = fs.readFileSync(rollupConfigPath, "utf8")
-
-// Edit imports
-rollupConfig = rollupConfig.replace(`'rollup-plugin-terser';`, `'rollup-plugin-terser';
-import sveltePreprocess from 'svelte-preprocess';
-import typescript from '@rollup/plugin-typescript';`)
-
-// Replace name of entry point
-rollupConfig = rollupConfig.replace(`'src/main.js'`, `'src/main.ts'`)
-
-// Add preprocessor
-rollupConfig = rollupConfig.replace(
-  'compilerOptions:',
-  'preprocess: sveltePreprocess({ sourceMap: !production }),\n\t\t\tcompilerOptions:'
-);
-
-// Add TypeScript
-rollupConfig = rollupConfig.replace(
-  'commonjs(),',
-  'commonjs(),\n\t\ttypescript({\n\t\t\tsourceMap: !production,\n\t\t\tinlineSources: !production\n\t\t}),'
-);
-fs.writeFileSync(rollupConfigPath, rollupConfig)
-
-// Add TSConfig
-const tsconfig = `{
-  "extends": "@tsconfig/svelte/tsconfig.json",
-
-  "include": ["src/**/*"],
-  "exclude": ["node_modules/*", "__sapper__/*", "public/*"]
-}`
-const tsconfigPath =  path.join(projectRoot, "tsconfig.json")
-fs.writeFileSync(tsconfigPath, tsconfig)
-
-// Add global.d.ts
-const dtsPath =  path.join(projectRoot, "src", "global.d.ts")
-fs.writeFileSync(dtsPath, `/// <reference types="svelte" />`)
-
-// Delete this script, but not during testing
-if (!argv[2]) {
-  // Remove the script
-  fs.unlinkSync(path.join(__filename))
-
-  // Check for Mac's DS_store file, and if it's the only one left remove it
-  const remainingFiles = fs.readdirSync(path.join(__dirname))
-  if (remainingFiles.length === 1 && remainingFiles[0] === '.DS_store') {
-    fs.unlinkSync(path.join(__dirname, '.DS_store'))
-  }
-
-  // Check if the scripts folder is empty
-  if (fs.readdirSync(path.join(__dirname)).length === 0) {
-    // Remove the scripts folder
-    fs.rmdirSync(path.join(__dirname))
-  }
-}
-
-// Adds the extension recommendation
-fs.mkdirSync(path.join(projectRoot, ".vscode"), { recursive: true })
-fs.writeFileSync(path.join(projectRoot, ".vscode", "extensions.json"), `{
-  "recommendations": ["svelte.svelte-vscode"]
-}
-`)
-
-console.log("Converted to TypeScript.")
-
-if (fs.existsSync(path.join(projectRoot, "node_modules"))) {
-  console.log("\nYou will need to re-run your dependency manager to get started.")
-}
diff --git a/src/App.svelte b/src/App.svelte
deleted file mode 100644
index 8373baf..0000000
--- a/src/App.svelte
+++ /dev/null
@@ -1,34 +0,0 @@
-<script>
-	import Header from './Header.svelte';
-	import Education from './Education.svelte';
-	import Experience from "./Experience.svelte";
-	import Skills from './Skills.svelte';
-	import Projects from './Projects.svelte';
-	import Extracurricular from './Extracurricular.svelte';
-	import Tiny from './Tiny.svelte'
-</script>
-
-<Header />
-<main>
-	<div style="width: 50%">
-		<Tiny/>
-		<Experience />
-		<Extracurricular />
-	</div>
-
-	<div style="width: 45%;">
-		<div class="tiny-adjust"></div>
-		<Education />
-		<Skills />
-		<Projects />
-	</div>
-</main>
-
-<style>
-	main {
-		margin: 0;
-		padding: 0;
-		display: flex;
-		justify-content: space-between
-	}
-</style>
\ No newline at end of file
diff --git a/src/Education.svelte b/src/Education.svelte
deleted file mode 100644
index e099c4e..0000000
--- a/src/Education.svelte
+++ /dev/null
@@ -1,39 +0,0 @@
-<script>
-    import Title from './Title.svelte';
-
-    const getEdu = async () => {
-            let res = await fetch("./education.json");
-            let edus = await res.json();
-            return edus;
-        }
-    const eduPromise = getEdu();
-</script>
-
-
-<div class="education">
-    <Title name="Education"/>
-    {#await eduPromise}
-        <p>Loading education...</p>
-    {:then edus}
-        {#each edus as edu}
-        <div class="education">
-            <h3 class="primary-heading">{edu.degree}</h3>
-            <h4 class="secondary-heading">{edu.school}</h4>
-            <div class="icon-row" style="width: 100%;">
-                <span>
-                    <i class="fa fa-calendar"></i>
-                    {edu.duration}
-                </span>
-            </div>
-            <ul>
-                {#each edu.points as point}
-                <li class="point-text">- {@html point.text}</li>
-                {/each}
-            </ul>   
-        </div>
-        {#if edu != edus[edus.length - 1]}
-        <div class="dotted-separator"></div>            
-        {/if}
-        {/each}     
-    {/await}
-</div>
diff --git a/src/Experience.svelte b/src/Experience.svelte
deleted file mode 100644
index 581b342..0000000
--- a/src/Experience.svelte
+++ /dev/null
@@ -1,42 +0,0 @@
-<script>
-    import Title from "./Title.svelte";
-	const getJobs = async () => {
-		let res = await fetch("./experience.json");
-	    let jobs = await res.json();
-    	return jobs;
-	}
-	const jobPromise = getJobs();
-    
-</script>
-
-<div class="experience">
-    <Title name="Experience"/>
-    {#await jobPromise}
-        <p>Loading experience...</p>
-    {:then jobs} 
-        {#each jobs as job}
-        <div class="job">
-            <h3 class="primary-heading">{job.title}</h3>
-            <h4 class="secondary-heading">{job.company}</h4>
-            <div class="icon-row" style="width: 100%;">
-                <span>
-                    <i class="fa fa-calendar"></i>
-                    {job.time}
-                </span>
-                <span>
-                    <i class="fa fa-map-marker"></i>
-                    {job.location}
-                </span>
-            </div>
-            <ul>
-            {#each job.points as point}
-                <li class="point-text">- {@html point.text}</li>
-            {/each}
-            </ul>
-        </div>
-        {#if job != jobs[jobs.length - 1]}
-        <div class="dotted-separator"></div>            
-        {/if}
-        {/each}
-    {/await}
-</div>
diff --git a/src/Extracurricular.svelte b/src/Extracurricular.svelte
deleted file mode 100644
index 792b3e3..0000000
--- a/src/Extracurricular.svelte
+++ /dev/null
@@ -1,23 +0,0 @@
-<script>
-    import Title from "./Title.svelte";
-	const getExtra = async () => {
-		let res = await fetch("./extracurricular.json");
-	    let extras = await res.json();
-    	return extras;
-	}
-	const extraPromise = getExtra();
-    
-</script>
-
-<div class="extracurricular">
-    <Title name="Extracurricular"/>
-    {#await extraPromise}
-        <p>Loading extracurricular...</p>
-    {:then extras} 
-        <ul>
-            {#each extras as extra}
-                <li class="point-text">- <b>{extra.title}</b>: {@html extra.description}</li>
-            {/each}
-        </ul>
-    {/await}
-</div>
diff --git a/src/Header.svelte b/src/Header.svelte
deleted file mode 100644
index 713ce42..0000000
--- a/src/Header.svelte
+++ /dev/null
@@ -1,57 +0,0 @@
-<script>
-    const getInfo = async () => {
-        let res = await fetch("./header.json");
-        let info = await res.json();
-        return info;
-    }
-    const infoPromise = getInfo();
-</script>
-{#await infoPromise}
-    <p>Loading info...</p>
-{:then info} 
-<h1>{info.name}</h1>
-<div class="links">
-    <span>
-        <i class="fa fa-envelope"></i>
-        <a href="mailto:{info.email}" target="_blank">{info.email}</a>
-    </span>
-    <span>
-        <i class="fa fa-phone"></i>
-        {info.phone}
-    </span>
-    <span>
-        <i class="fa fa-link"></i>
-        <a href="https://{info.website}" target="_blank">{info.website}</a>
-    </span>
-    <span>
-        <i class="fa fa-github"></i>
-        <a href="https://github.com/{info.github}" target="_blank">{info.github}</a>
-    </span>
-    <span>
-        <i class="fa fa-linkedin"></i>
-        <a href="https://linkedin.com/in/{info.linkedin}" target="_blank">{info.linkedin}</a>
-    </span>
-</div>    
-{/await}
-
-
-<style>
-    h1{
-        font-size: 2.5rem;
-        font-weight: 500;
-        text-transform: uppercase;
-        margin-bottom: 15px;
-    }
-
-    .links {
-        display: flex;
-        justify-content: left;
-        padding-bottom: 15px;
-    }
-    .links > span {
-        font-size: 12px;
-        font-weight: 600;
-        padding-right: 15px;
-    }
-
-</style>
\ No newline at end of file
diff --git a/src/Projects.svelte b/src/Projects.svelte
deleted file mode 100644
index c754e10..0000000
--- a/src/Projects.svelte
+++ /dev/null
@@ -1,37 +0,0 @@
-<script>
-    import Title from './Title.svelte';
-    const getProjects = async () => {
-		let res = await fetch("./projects.json");
-	    let projects = await res.json();
-    	return projects;
-	}
-	const projectPromise = getProjects();
-
-</script>
-
-<div class="projects">
-    <Title name="Projects"/>
-    {#await projectPromise}
-    <p>Loading projects...</p>        
-    {:then projects} 
-        {#each projects as project}
-        <div class="project">
-            <h4 class="secondary-heading"><a href="{project.link}"><i class="fa fa-github"></i>{project.title}</a></h4>
-            <ul>
-            {#each project.points as point}
-                <li class="point-text">- {@html point.text}</li>
-            {/each}
-            </ul>
-        </div>
-        {#if project != projects[projects.length - 1]}
-        <div class="dotted-separator"></div>            
-        {/if}
-        {/each}
-    {/await}
-</div>
-
-<style>
-    .project {
-        padding-bottom: 3px;
-    }
-</style>
\ No newline at end of file
diff --git a/src/Resume.svelte b/src/Resume.svelte
new file mode 100644
index 0000000..ee224b7
--- /dev/null
+++ b/src/Resume.svelte
@@ -0,0 +1,66 @@
+<script lang="ts">
+  import type { Resume } from "./lib/types";
+
+  import Header from "./components/Header.svelte";
+  import Tiny from "./components/Tiny.svelte";
+  import Section from "./components/Section.svelte";
+  import Education from "./components/sections/Education.svelte";
+  import Experience from "./components/sections/Experience.svelte";
+  import Extracurricular from "./components/sections/Extracurricular.svelte";
+  import Skills from "./components/sections/Skills.svelte";
+  import Projects from "./components/sections/Projects.svelte";
+
+  // Import resume data
+  import data from "./resume.json";
+</script>
+
+<Header info={data.info}></Header>
+
+<main>
+  <div style="width: 50%">
+    {#if data.tiny_text}
+      <Tiny>{data.tiny_text}</Tiny>
+    {/if}
+
+    {#if data.experience}
+      <Section name="Experience">
+        <Experience items={data.experience} />
+      </Section>
+    {/if}
+
+    {#if data.extracurricular}
+      <Section name="Extracurricular">
+        <Extracurricular items={data.extracurricular} />
+      </Section>
+    {/if}
+  </div>
+
+  <div style="width: 45%;">
+    {#if data.education}
+      <Section name="Education">
+        <Education items={data.education} />
+      </Section>
+    {/if}
+
+    {#if data.skills}
+      <Section name="Skills">
+        <Skills items={data.skills} />
+      </Section>
+    {/if}
+
+    {#if data.projects}
+      <Section name="Projects">
+        <Projects items={data.projects} />
+      </Section>
+    {/if}
+  </div>
+</main>
+
+<style>
+  main {
+    margin: 0;
+    padding: 0;
+    display: flex;
+    justify-content: space-between;
+  }
+</style>
diff --git a/src/Skills.svelte b/src/Skills.svelte
deleted file mode 100644
index afb20ef..0000000
--- a/src/Skills.svelte
+++ /dev/null
@@ -1,29 +0,0 @@
-<script>
-    import Title from "./Title.svelte";
-	const getSkills = async () => {
-		let res = await fetch("./skills.json");
-	    let skills = await res.json();
-    	return skills;
-	}
-	const skillsPromise = getSkills();
-    
-</script>
-
-<div class="skills">
-    <Title name="Skills"/>
-    {#await skillsPromise}
-        <p>Loading skills...</p>
-    {:then skills}
-        <ul>
-            {#each skills as skill}
-                <li class="skill"><b>{skill.type}</b>: {skill.list}</li>
-            {/each}
-        </ul>
-    {/await}
-</div>
-
-<style>
-    .skill{
-        font-size: 14px;
-    }
-</style>
\ No newline at end of file
diff --git a/src/Tiny.svelte b/src/Tiny.svelte
deleted file mode 100644
index 106d8fd..0000000
--- a/src/Tiny.svelte
+++ /dev/null
@@ -1,25 +0,0 @@
-<script>
-    const getText = async () => {
-            let res = await fetch("./text.json");
-            let text = await res.json();
-            return text;
-        }
-    const textPromise = getText();
-</script>
-
-
-<div class="tiny">
-    {#await textPromise}
-        <p>Loading tiny text...</p>
-    {:then text} 
-        <p>{text.text}</p>
-    {/await}
-</div>
-
-<style>
-    .tiny, p{
-        font-size: 1px;
-        color: #fff;
-        -webkit-print-color-adjust: exact
-    }
-</style>
\ No newline at end of file
diff --git a/src/Title.svelte b/src/Title.svelte
deleted file mode 100644
index 93d6248..0000000
--- a/src/Title.svelte
+++ /dev/null
@@ -1,13 +0,0 @@
-<script>
-    export let name;
-</script>
-
-<h1>{name}</h1>
-<hr>
-<style>
-    h1 {
-        font-weight: 600;
-        text-transform: uppercase;
-        font-size: 1.5rem;
-    }
-</style>
\ No newline at end of file
diff --git a/src/components/DetailItem.svelte b/src/components/DetailItem.svelte
new file mode 100644
index 0000000..af181d5
--- /dev/null
+++ b/src/components/DetailItem.svelte
@@ -0,0 +1,7 @@
+<li class="item-text">- <slot /></li>
+
+<style>
+  .item-text {
+    font-size: 12px;
+  }
+</style>
diff --git a/src/components/DetailList.svelte b/src/components/DetailList.svelte
new file mode 100644
index 0000000..cbb46b2
--- /dev/null
+++ b/src/components/DetailList.svelte
@@ -0,0 +1,3 @@
+<ul>
+  <slot />
+</ul>
diff --git a/src/components/DottedRule.svelte b/src/components/DottedRule.svelte
new file mode 100644
index 0000000..aa8c464
--- /dev/null
+++ b/src/components/DottedRule.svelte
@@ -0,0 +1,9 @@
+<div class="dotted-separator"></div>
+
+<style>
+  .dotted-separator {
+    border: none;
+    border-top: 1px dashed #888;
+    margin: 10px 0px;
+  }
+</style>
diff --git a/src/components/Header.svelte b/src/components/Header.svelte
new file mode 100644
index 0000000..254db0d
--- /dev/null
+++ b/src/components/Header.svelte
@@ -0,0 +1,67 @@
+<script lang="ts">
+  import type { Info } from "../lib/types";
+
+  export let info: Info;
+</script>
+
+<h1>{info.name}</h1>
+
+<div class="links">
+  <span>
+    <i class="fa fa-envelope"></i>
+    <a href="mailto:{info.email}" target="_blank">{info.email}</a>
+  </span>
+
+  {#if info.phone}
+    <span>
+      <i class="fa fa-phone"></i>
+      {info.phone}
+    </span>
+  {/if}
+
+  {#if info.website}
+    <span>
+      <i class="fa fa-link"></i>
+      <a href="https://{info.website}" target="_blank">{info.website}</a>
+    </span>
+  {/if}
+
+  {#if info.github}
+    <span>
+      <i class="fa fa-github"></i>
+      <a href="https://github.com/{info.github}" target="_blank">
+        {info.github}
+      </a>
+    </span>
+  {/if}
+
+  {#if info.linkedin}
+    <span>
+      <i class="fa fa-linkedin"></i>
+      <a href="https://linkedin.com/in/{info.linkedin}" target="_blank">
+        {info.linkedin}
+      </a>
+    </span>
+  {/if}
+</div>
+
+<style>
+  h1 {
+    font-size: 2.5rem;
+    font-weight: 500;
+    text-transform: uppercase;
+    margin-bottom: 15px;
+  }
+
+  .links {
+    display: flex;
+    justify-content: left;
+    padding-bottom: 15px;
+  }
+
+  .links > span {
+    font-size: 12px;
+    font-weight: 600;
+    padding-right: 15px;
+  }
+</style>
diff --git a/src/components/LoadingSpinner.svelte b/src/components/LoadingSpinner.svelte
new file mode 100644
index 0000000..3b4e92e
--- /dev/null
+++ b/src/components/LoadingSpinner.svelte
@@ -0,0 +1,42 @@
+<script lang="ts">
+  import type { HTMLAttributes } from "svelte/elements";
+
+  export let color: string = "gray";
+</script>
+
+<div class="spinner" style={"color: " + color}></div>
+
+<style>
+  .spinner,
+  .spinner:after {
+    box-sizing: border-box;
+  }
+
+  .spinner {
+    display: inline-block;
+    width: 80px;
+    height: 80px;
+  }
+
+  .spinner:after {
+    content: " ";
+    display: block;
+    width: 64px;
+    height: 64px;
+    margin: 8px;
+    border-radius: 50%;
+    border: 6.4px solid currentColor;
+    border-color: currentColor transparent currentColor transparent;
+    animation: spin 1.2s linear infinite;
+  }
+
+  @keyframes spin {
+    0% {
+      transform: rotate(0deg);
+    }
+
+    100% {
+      transform: rotate(360deg);
+    }
+  }
+</style>
diff --git a/src/components/PrimaryHeading.svelte b/src/components/PrimaryHeading.svelte
new file mode 100644
index 0000000..36cd519
--- /dev/null
+++ b/src/components/PrimaryHeading.svelte
@@ -0,0 +1,9 @@
+<h3 class="primary-heading"><slot /></h3>
+
+<style>
+  h3.primary-heading {
+    font-size: 16px;
+    font-weight: 400;
+    margin: 5px 0px;
+  }
+</style>
diff --git a/src/components/SecondaryHeading.svelte b/src/components/SecondaryHeading.svelte
new file mode 100644
index 0000000..e7d14ac
--- /dev/null
+++ b/src/components/SecondaryHeading.svelte
@@ -0,0 +1,9 @@
+<h4 class="secondary-heading"><slot /></h4>
+
+<style>
+  h4.secondary-heading {
+    font-size: 14px;
+    font-weight: 600;
+    margin: 5px 0px;
+  }
+</style>
diff --git a/src/components/Section.svelte b/src/components/Section.svelte
new file mode 100644
index 0000000..a902ec7
--- /dev/null
+++ b/src/components/Section.svelte
@@ -0,0 +1,17 @@
+<script lang="ts">
+  export let name: string;
+</script>
+
+<div class={name}>
+  <h1>{name || ""}</h1>
+  <hr />
+  <slot />
+</div>
+
+<style>
+  h1 {
+    font-weight: 600;
+    text-transform: uppercase;
+    font-size: 1.5rem;
+  }
+</style>
diff --git a/src/components/Tiny.svelte b/src/components/Tiny.svelte
new file mode 100644
index 0000000..51534bb
--- /dev/null
+++ b/src/components/Tiny.svelte
@@ -0,0 +1,23 @@
+<p class="tiny">
+  <slot />
+</p>
+
+<style>
+  p.tiny {
+    --height: 3px;
+    --font-size: calc(var(--height) / 3);
+
+    font-size: var(--font-size);
+    height: var(--height);
+    overflow: hidden;
+
+    color: #fff;
+
+    -webkit-print-color-adjust: exact;
+    print-color-adjust: exact;
+
+    margin: 0px;
+    margin-top: calc(-1 * var(--height));
+    padding: 0px;
+  }
+</style>
diff --git a/src/components/sections/Education.svelte b/src/components/sections/Education.svelte
new file mode 100644
index 0000000..b29d6a0
--- /dev/null
+++ b/src/components/sections/Education.svelte
@@ -0,0 +1,38 @@
+<script lang="ts">
+  import type { Education } from "../../lib/types";
+  import { dateRange } from "../../lib/util";
+
+  import PrimaryHeading from "../PrimaryHeading.svelte";
+  import SecondaryHeading from "../SecondaryHeading.svelte";
+  import DetailList from "../DetailList.svelte";
+  import DetailItem from "../DetailItem.svelte";
+  import DottedRule from "../DottedRule.svelte";
+
+  export let items: Education[];
+</script>
+
+{#each items as education, index}
+  <div class="education">
+    <PrimaryHeading>{education.certification}</PrimaryHeading>
+    <SecondaryHeading>{education.location}</SecondaryHeading>
+
+    <div class="icon-row" style="width: 100%;">
+      <span>
+        <i class="fa fa-calendar"></i>
+        {dateRange(education.start, education.end)}
+      </span>
+    </div>
+
+    {#if education.details}
+      <DetailList>
+        {#each education.details as detail}
+          <DetailItem>{@html detail}</DetailItem>
+        {/each}
+      </DetailList>
+    {/if}
+  </div>
+
+  {#if index < items.length - 1}
+    <DottedRule />
+  {/if}
+{/each}
diff --git a/src/components/sections/Experience.svelte b/src/components/sections/Experience.svelte
new file mode 100644
index 0000000..30da597
--- /dev/null
+++ b/src/components/sections/Experience.svelte
@@ -0,0 +1,42 @@
+<script lang="ts">
+  import type { Experience } from "../../lib/types";
+  import { dateRange } from "../../lib/util";
+  import DetailItem from "../DetailItem.svelte";
+  import DetailList from "../DetailList.svelte";
+  import DottedRule from "../DottedRule.svelte";
+  import PrimaryHeading from "../PrimaryHeading.svelte";
+  import SecondaryHeading from "../SecondaryHeading.svelte";
+
+  export let items: Experience[];
+</script>
+
+{#each items as position, index}
+  <div class="job">
+    <PrimaryHeading>{position.title}</PrimaryHeading>
+    <SecondaryHeading>{position.company}</SecondaryHeading>
+
+    <div class="icon-row" style="width: 100%;">
+      <span>
+        <i class="fa fa-calendar"></i>
+        {dateRange(position.start, position.end)}
+      </span>
+
+      {#if position.location}
+        <span>
+          <i class="fa fa-map-marker"></i>
+          {position.location}
+        </span>
+      {/if}
+    </div>
+
+    <DetailList>
+      {#each position.details as detailLine}
+        <DetailItem>{@html detailLine}</DetailItem>
+      {/each}
+    </DetailList>
+  </div>
+
+  {#if index < items.length - 1}
+    <DottedRule />
+  {/if}
+{/each}
diff --git a/src/components/sections/Extracurricular.svelte b/src/components/sections/Extracurricular.svelte
new file mode 100644
index 0000000..81ba681
--- /dev/null
+++ b/src/components/sections/Extracurricular.svelte
@@ -0,0 +1,16 @@
+<script lang="ts">
+  import type { Extracurricular } from "../../lib/types";
+  import DetailItem from "../DetailItem.svelte";
+  import DetailList from "../DetailList.svelte";
+
+  export let items: Extracurricular[];
+</script>
+
+<DetailList>
+  {#each items as item}
+    <DetailItem>
+      <b>{[item.title, item.location].filter((x) => x).join(" | ")}</b>: {@html item.description}
+    </DetailItem>
+  {/each}
+</DetailList>
+<ul></ul>
diff --git a/src/components/sections/Projects.svelte b/src/components/sections/Projects.svelte
new file mode 100644
index 0000000..124cbff
--- /dev/null
+++ b/src/components/sections/Projects.svelte
@@ -0,0 +1,36 @@
+<script lang="ts">
+  import type { Project } from "../../lib/types";
+  import DetailItem from "../DetailItem.svelte";
+  import DetailList from "../DetailList.svelte";
+  import DottedRule from "../DottedRule.svelte";
+  import SecondaryHeading from "../SecondaryHeading.svelte";
+
+  export let items: Project[];
+</script>
+
+{#each items as project, index}
+  <div class="project">
+    <SecondaryHeading>
+      <a href={project.link}>
+        <i class="fa fa-github"></i>
+        {project.title}
+      </a>
+    </SecondaryHeading>
+
+    <DetailList>
+      {#each project.details as detail}
+        <DetailItem>{@html detail}</DetailItem>
+      {/each}
+    </DetailList>
+  </div>
+
+  {#if index < items.length - 1}
+    <DottedRule />
+  {/if}
+{/each}
+
+<style>
+  .project {
+    padding-bottom: 3px;
+  }
+</style>
diff --git a/src/components/sections/Skills.svelte b/src/components/sections/Skills.svelte
new file mode 100644
index 0000000..61da324
--- /dev/null
+++ b/src/components/sections/Skills.svelte
@@ -0,0 +1,21 @@
+<script lang="ts">
+  import type { Skill } from "../../lib/types";
+  import DetailList from "../DetailList.svelte";
+
+  export let items: Skill[];
+</script>
+
+<DetailList>
+  {#each items as skill}
+    <ul class="skill">
+      <b>{skill.category}</b>:
+      {skill.items.join(", ")}
+    </ul>
+  {/each}
+</DetailList>
+
+<style>
+  .skill {
+    font-size: 14px;
+  }
+</style>
diff --git a/src/global.css b/src/global.css
new file mode 100644
index 0000000..89f44f0
--- /dev/null
+++ b/src/global.css
@@ -0,0 +1,50 @@
+html,
+body {
+  margin: 0;
+  padding: 0;
+  position: relative;
+}
+
+body {
+  color: #000;
+  margin: 0.25in 0.5in;
+  padding: 8px;
+  box-sizing: border-box;
+  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
+    Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
+}
+
+h1 {
+  margin: 0;
+  padding: 0;
+}
+
+hr {
+  border-top: solid 4px #000 !important;
+  margin: 4px;
+}
+
+a {
+  text-decoration: none;
+  color: #000;
+}
+
+.icon-row {
+  display: flex;
+  justify-content: space-between;
+}
+
+.icon-row > span {
+  font-size: 12px;
+  padding-right: 20px;
+}
+
+i {
+  margin-right: 3px;
+}
+
+ul {
+  padding-left: 0;
+  margin: 5px 0px;
+  list-style-type: none;
+}
diff --git a/src/lib/types.ts b/src/lib/types.ts
new file mode 100644
index 0000000..f54b8b4
--- /dev/null
+++ b/src/lib/types.ts
@@ -0,0 +1,52 @@
+export interface Resume {
+  info: Info;
+  tiny_text?: string;
+  education?: Education[];
+  experience?: Experience[];
+  extracurricular?: Extracurricular[];
+  projects?: Project[];
+  skills?: Skill[];
+}
+
+export interface Education {
+  certification: string;
+  location: string;
+  details?: string[];
+  start: string;
+  end?: string;
+}
+
+export interface Experience {
+  title: string;
+  company: string;
+  location: string;
+  details: string[];
+  start: string;
+  end?: string;
+}
+
+export interface Extracurricular {
+  title: string;
+  description: string;
+  location?: string;
+}
+
+export interface Info {
+  name: string;
+  website?: string;
+  email: string;
+  phone?: string;
+  github?: string;
+  linkedin?: string;
+}
+
+export interface Project {
+  title: string;
+  link: string;
+  details: string[];
+}
+
+export interface Skill {
+  category: string;
+  items: string[];
+}
diff --git a/src/lib/util.ts b/src/lib/util.ts
new file mode 100644
index 0000000..202e15a
--- /dev/null
+++ b/src/lib/util.ts
@@ -0,0 +1,7 @@
+export const dateRange = (start: string, end: string | undefined): string => {
+  if (!end) {
+    return start;
+  }
+
+  return start + " - " + end;
+};
diff --git a/src/main.js b/src/main.js
deleted file mode 100644
index d80e9a3..0000000
--- a/src/main.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import App from './App.svelte';
-
-const app = new App({
-	target: document.body,
-});
-
-export default app;
\ No newline at end of file
diff --git a/src/main.ts b/src/main.ts
new file mode 100644
index 0000000..3536797
--- /dev/null
+++ b/src/main.ts
@@ -0,0 +1,8 @@
+import "./global.css";
+import Resume from "./Resume.svelte";
+
+const resume = new Resume({
+  target: document.getElementById("app")!,
+});
+
+export default resume;
diff --git a/src/resume.json b/src/resume.json
new file mode 100644
index 0000000..e14c065
--- /dev/null
+++ b/src/resume.json
@@ -0,0 +1,192 @@
+{
+    "$schema": "resume.schema.json",
+    "info": {
+        "name": "Jeremie Bornais",
+        "email": "jeremiejbornais@gmail.com",
+        "phone": "(226) 350-2944",
+        "website": "jeremie.bornais.ca",
+        "github": "jere-mie",
+        "linkedin": "jeremie-bornais"
+    },
+    "tiny_text": "Bachelors of Computer Science (Honours), University of Windsor, September 2019 - October 2023, 97.25% GPA",
+    "education": [
+        {
+            "location": "University of Windsor",
+            "certification": "Bachelors of Computer Science (Honours)",
+            "start": "September 2019",
+            "end": "October 2023",
+            "details": [
+                "<b>97.40%</b> Major Average, <b>97.25%</b> Cumulative Average",
+                "Recipient of the President's Scholarship, Eleanor Catherine Wallace Memorial Scholarship, and Foresters Scholarship"
+            ]
+        },
+        {
+            "location": "Business Career College",
+            "certification": "Life License Qualification Program (LLQP)",
+            "start": "August 2023",
+            "end": "October 2023",
+            "details": [
+                "Completed the LLQP program to obtain my Ontario Life Insurance and Accident & Sickness Insurance licenses"
+            ]
+        }
+    ],
+    "experience": [
+        {
+            "title": "Software Engineer Intern",
+            "company": "Publicis Sapient",
+            "start": "June 2022",
+            "end": "August 2022",
+            "location": "Toronto, ON",
+            "details": [
+                "Created and deployed a <b>GraphQL</b> API using <b>Java, Spring Boot, and PostgreSQL</b> for a personal budgeting mobile app.",
+                "Used <b>GitHub Actions</b> to set up automated <b>CI/CD</b> workflows."
+            ]
+        },
+        {
+            "title": "Software Developer Intern",
+            "company": "Assent Inc.",
+            "start": "January 2022",
+            "end": "April 2022",
+            "location": "Ottawa, ON",
+            "details": [
+                "Used <b>Angular and SCSS</b> to create and update front-end components, adhering to strict styling standards.",
+                "Used <b>C# and .NET Core</b> to create API endpoints, unit tests, component tests, and integration tests."
+            ]
+        },
+        {
+            "title": "Software Developer Intern",
+            "company": "Green Shield Canada",
+            "start": "May 2021",
+            "end": "August 2021",
+            "location": "Windsor, ON",
+            "details": [
+                "Used <b>Oracle PL/SQL</b> to create Data Reports and Extracts"
+            ]
+        },
+        {
+            "title": "Research Software Developer",
+            "company": "University of Windsor",
+            "start": "June 2020",
+            "end": "April 2023",
+            "location": "Windsor, ON",
+            "details": [
+                "Developed a <b>web application</b> for the analysis and presentation of experimental SAS data",
+                "Achieved <b>100X faster compute time</b> of select elements",
+                "Using <b>Flask, Bootstrap, NumPy, SciPy, and Matplotlib</b>"
+            ]
+        },
+        {
+            "title": "Teaching Assistant",
+            "company": "University of Windsor",
+            "start": "September 2020",
+            "end": "June 2023",
+            "location": "Windsor, ON",
+            "details": [
+                "Responsible for lab instruction, marking, and holding office hours for <b>4 core undergraduate courses</b>",
+                "<b>Built and deployed a web application</b> that grades assignments</b>"
+            ]
+        }
+    ],
+    "skills": [
+        {
+            "category": "Programming",
+            "items": [
+                "Python",
+                "JavaScript",
+                "Go",
+                "Java",
+                "C#",
+                "HTML",
+                "CSS",
+                "SCSS",
+                "C++",
+                "SQL",
+                "C",
+                "Ruby"
+            ]
+        },
+        {
+            "category": "Frameworks",
+            "items": [
+                "Flask",
+                "React Native",
+                "Svelte",
+                "Angular",
+                "React.js",
+                "Gin",
+                ".NET Core",
+                "Spring Boot",
+                "Bootstrap",
+                "Ruby on Rails"
+            ]
+        },
+        {
+            "category": "Technologies",
+            "items": [
+                "Git",
+                "Linux",
+                "Docker",
+                "Caddy Server",
+                "GraphQL",
+                "REST API",
+                "OAuth2",
+                "CI/CD",
+                "GitHub Actions"
+            ]
+        },
+        {
+            "category": "Cloud",
+            "items": [
+                "Google Cloud Platform",
+                "Microsoft Azure",
+                "DigitalOcean",
+                "Heroku"
+            ]
+        }
+    ],
+    "projects": [
+        {
+            "title": "Automagic Websites",
+            "link": "https://github.com/jere-mie/automagic-websites",
+            "details": [
+                "Full stack web application featuring <b>user authentication</b>",
+                "Allows users to <b>create, host, and export</b> their own personal website",
+                "Made with <b>Python, Flask, SQLAlchemy, Bootstrap</b>"
+            ]
+        },
+        {
+            "title": "UWindsor API",
+            "link": "https://github.com/jere-mie/uwindsor-api",
+            "details": [
+                "<b>REST API</b> created to enable developers to create applications for the UWindsor community",
+                "Serves course, staff and building data related to the University of Windsor",
+                "Made with <b>Go, Gin, and SQLX</b>"
+            ]
+        },
+        {
+            "title": "Easy-MASM",
+            "link": "https://github.com/jere-mie/easy-masm",
+            "details": [
+                "Automated system for assembling, linking, and executing <b>MASM32</b> programs",
+                "Used by over <b>500 students</b>",
+                "Made with <b>Docker, bash and batch</b>"
+            ]
+        }
+    ],
+    "extracurricular": [
+        {
+            "title": "President",
+            "description": "Led a team of 20 volunteers to create applications, organize events, and represent over 900 undergraduate students (2021-2023)"
+        },
+        {
+            "title": "Co-Founder and Technical Lead",
+            "description": "MLH Hackathon (winhacks.ca) (2020-2022)",
+            "location": "WinHacks"
+        },
+        {
+            "title": "Co-Founder and Technical Lead",
+            "description": "MLH Hackathon (borderhacks.com) (2020-2021)",
+            "location": "BorderHacks"
+        }
+    ]
+}
diff --git a/src/resume.schema.json b/src/resume.schema.json
new file mode 100644
index 0000000..171902e
--- /dev/null
+++ b/src/resume.schema.json
@@ -0,0 +1,192 @@
+{
+    "$comment": "Schema for resume.json",
+    "properties": {
+        "education": {
+            "items": {
+                "properties": {
+                    "certification": {
+                        "description": "The name of the certification, such as 'Bachelors of Computer Science (Honours)'",
+                        "type": "string"
+                    },
+                    "details": {
+                        "$ref": "#/definitions/array[string]",
+                        "description": "Extra information about the education/certification"
+                    },
+                    "end": {
+                        "description": "The month/year you finished receiving education",
+                        "type": "string"
+                    },
+                    "location": {
+                        "description": "Where the education was received, such as a university or bootcamp",
+                        "type": "string"
+                    },
+                    "start": {
+                        "description": "The month/year you started receiving education",
+                        "type": "string"
+                    }
+                },
+                "required": [
+                    "location",
+                    "certification",
+                    "start"
+                ],
+                "type": "object"
+            },
+            "minItems": 1,
+            "type": "array"
+        },
+        "experience": {
+            "items": {
+                "properties": {
+                    "company": {
+                        "description": "The company you worked for",
+                        "type": "string"
+                    },
+                    "details": {
+                        "$ref": "#/definitions/array[string]",
+                        "description": "Extra information about the experience, such as tools used or quantative improvements made"
+                    },
+                    "end": {
+                        "description": "When you ended",
+                        "type": "string"
+                    },
+                    "location": {
+                        "description": "The location you worked from",
+                        "type": "string"
+                    },
+                    "title": {
+                        "description": "The title you held",
+                        "type": "string"
+                    },
+                    "start": {
+                        "description": "When you started",
+                        "type": "string"
+                    }
+                },
+                "required": [
+                    "company",
+                    "details",
+                    "start",
+                    "title"
+                ],
+                "type": "object"
+            },
+            "minItems": 1,
+            "type": "array"
+        },
+        "extracurricular": {
+            "items": {
+                "properties": {
+                    "description": {
+                        "description": "Description of the extracurricular activity",
+                        "type": "string"
+                    },
+                    "location": {
+                        "description": "Location of the extracurricular activity",
+                        "type": "string"
+                    },
+                    "title": {
+                        "description": "Title of the extracurricular activity",
+                        "type": "string"
+                    }
+                },
+                "required": [
+                    "title",
+                    "description"
+                ],
+                "type": "object"
+            },
+            "minItems": 1,
+            "type": "array"
+        },
+        "info": {
+            "description": "The most important information on your resume, such as name and contact.",
+            "properties": {
+                "email": {
+                    "description": "Your email address",
+                    "type": "string"
+                },
+                "github": {
+                    "description": "Your GitHub profile name",
+                    "type": "string"
+                },
+                "linkedin": {
+                    "description": "Your LinkedIn profile name",
+                    "type": "string"
+                },
+                "name": {
+                    "description": "Your full name",
+                    "type": "string"
+                },
+                "phone": {
+                    "description": "Your phone number",
+                    "type": "string"
+                },
+                "website": {
+                    "description": "Your website. It is recommended you do not include http(s)://, for readability.",
+                    "type": "string"
+                }
+            },
+            "required": [
+                "name",
+                "email"
+            ],
+            "type": "object"
+        },
+        "projects": {
+            "items": {
+                "properties": {
+                    "details": {
+                        "$ref": "#/definitions/array[string]",
+                        "description": "Details about the project, such as which technologies you used."
+                    },
+                    "link": {
+                        "description": "Link to the project. If possible, the link should point to a live demo.",
+                        "type": "string"
+                    },
+                    "title": {
+                        "description": "The title of the project.",
+                        "type": "string"
+                    }
+                },
+                "required": [
+                    "title",
+                    "details"
+                ],
+                "type": "object"
+            },
+            "minItems": 1,
+            "type": "array"
+        },
+        "skills": {
+            "type": "array",
+            "items": {
+                "type": "object",
+                "properties": {
+                    "category": {
+                        "type": "string",
+                        "description": "The name of the skill category. For example, 'Languages'"
+                    },
+                    "items": {
+                        "$ref": "#/definitions/array[string]",
+                        "description": "The items in the skill category. For example, Java, C++, Python"
+                    }
+                }
+            },
+            "minItems": 1
+        },
+        "tiny_text": {
+            "description": "This text is imprinted near the top of the resume in tiny white text. Can be useful to optimize for automated systems.",
+            "type": "string"
+        }
+    },
+    "definitions": {
+        "array[string]": {
+            "default": [],
+            "items": {
+                "type": "string"
+            },
+            "type": "array"
+        }
+    }
+}
diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts
new file mode 100644
index 0000000..4078e74
--- /dev/null
+++ b/src/vite-env.d.ts
@@ -0,0 +1,2 @@
+/// <reference types="svelte" />
+/// <reference types="vite/client" />
diff --git a/svelte.config.js b/svelte.config.js
new file mode 100644
index 0000000..b0683fd
--- /dev/null
+++ b/svelte.config.js
@@ -0,0 +1,7 @@
+import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
+
+export default {
+  // Consult https://svelte.dev/docs#compile-time-svelte-preprocess
+  // for more information about preprocessors
+  preprocess: vitePreprocess(),
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..5fb548f
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,20 @@
+{
+  "extends": "@tsconfig/svelte/tsconfig.json",
+  "compilerOptions": {
+    "target": "ESNext",
+    "useDefineForClassFields": true,
+    "module": "ESNext",
+    "resolveJsonModule": true,
+    /**
+     * Typecheck JS in `.svelte` and `.js` files by default.
+     * Disable checkJs if you'd like to use dynamic types in JS.
+     * Note that setting allowJs false does not prevent the use
+     * of JS in `.svelte` files.
+     */
+    "allowJs": true,
+    "checkJs": true,
+    "isolatedModules": true
+  },
+  "include": ["src/**/*.ts", "src/**/*.js", "src/**/*.svelte"],
+  "references": [{ "path": "./tsconfig.node.json" }]
+}
diff --git a/tsconfig.node.json b/tsconfig.node.json
new file mode 100644
index 0000000..d02c37d
--- /dev/null
+++ b/tsconfig.node.json
@@ -0,0 +1,10 @@
+{
+  "compilerOptions": {
+    "composite": true,
+    "skipLibCheck": true,
+    "module": "ESNext",
+    "moduleResolution": "bundler",
+    "strict": true
+  },
+  "include": ["vite.config.ts"]
+}
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..5ac8ecf
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,7 @@
+import { defineConfig } from "vite";
+import { svelte } from "@sveltejs/vite-plugin-svelte";
+
+// https://vitejs.dev/config/
+export default defineConfig({
+  plugins: [svelte()],
+});