about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/golang-test-lint.yml4
-rw-r--r--.prettierignore4
-rw-r--r--Dockerfile.embedr1
-rw-r--r--bskyembed/src/index.css2
-rw-r--r--bskyweb/.gitignore4
-rw-r--r--bskyweb/static/css/.gitkeep0
-rw-r--r--bskyweb/templates/base.html381
-rw-r--r--package.json2
-rw-r--r--scripts/post-web-build.js25
-rw-r--r--src/App.web.tsx1
-rw-r--r--src/style.css403
-rw-r--r--web/index.html382
12 files changed, 440 insertions, 769 deletions
diff --git a/.github/workflows/golang-test-lint.yml b/.github/workflows/golang-test-lint.yml
index 36e28841d..c8124dbad 100644
--- a/.github/workflows/golang-test-lint.yml
+++ b/.github/workflows/golang-test-lint.yml
@@ -21,7 +21,7 @@ jobs:
         with:
           go-version: '1.22'
       - name: Dummy Static Files
-        run: touch bskyweb/static/js/blah.js && touch bskyweb/static/media/blah.txt
+        run: touch bskyweb/static/js/blah.js && touch bskyweb/static/css/blah.txt && touch bskyweb/static/media/blah.txt
       - name: Check
         run: cd bskyweb/ && make check
       - name: Build (binary)
@@ -38,6 +38,6 @@ jobs:
         with:
           go-version: '1.22'
       - name: Dummy Static Files
-        run: touch bskyweb/static/js/blah.js && touch bskyweb/static/media/blah.txt
+        run: touch bskyweb/static/js/blah.js && touch bskyweb/static/css/blah.txt && touch bskyweb/static/media/blah.txt
       - name: Lint
         run: cd bskyweb/ &&  make lint
diff --git a/.prettierignore b/.prettierignore
index 641a1b8bf..8ccbae214 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -1,4 +1,4 @@
-# Ignore everything except JS-ey code.
+# Ignore everything except JS-ey or CSS code.
 # Based on https://stackoverflow.com/a/70715829/458193
 *
 !**/*.js
@@ -7,6 +7,8 @@
 !**/*.tsx
 !*/
 
+!**/*.css
+
 # More specific ignores go below.
 .expo
 android
diff --git a/Dockerfile.embedr b/Dockerfile.embedr
index 9ff04aa5c..663cbcfc5 100644
--- a/Dockerfile.embedr
+++ b/Dockerfile.embedr
@@ -40,6 +40,7 @@ RUN find ./bskyweb/embedr-static && find ./bskyweb/embedr-templates && find ./bs
 
 # hack around issue with empty directory and go:embed
 RUN touch bskyweb/static/js/empty.txt
+RUN touch bskyweb/static/css/empty.txt
 RUN touch bskyweb/static/media/empty.txt
 
 #
diff --git a/bskyembed/src/index.css b/bskyembed/src/index.css
index 23457ec28..22b2b8be5 100644
--- a/bskyembed/src/index.css
+++ b/bskyembed/src/index.css
@@ -4,4 +4,4 @@
 
 .break-word {
   word-break: break-word;
-}
\ No newline at end of file
+}
diff --git a/bskyweb/.gitignore b/bskyweb/.gitignore
index 05b3ad7ab..a63a381f9 100644
--- a/bskyweb/.gitignore
+++ b/bskyweb/.gitignore
@@ -10,6 +10,10 @@ static/js/*.js
 static/js/*.map
 static/js/*.js.LICENSE.txt
 static/js/empty.txt
+static/css/*.css
+static/css/*.map
+static/css/*.css.LICENSE.txt
+static/css/empty.txt
 static/media/*.png
 static/media/empty.txt
 templates/scripts.html
diff --git a/bskyweb/static/css/.gitkeep b/bskyweb/static/css/.gitkeep
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/bskyweb/static/css/.gitkeep
diff --git a/bskyweb/templates/base.html b/bskyweb/templates/base.html
index 609c17c7c..eaa31aa4a 100644
--- a/bskyweb/templates/base.html
+++ b/bskyweb/templates/base.html
@@ -32,387 +32,6 @@
   <link rel="preload" as="font" type="font/otf" href="/static/media/Inter-BlackItalic.27b9f0ad06fd13a7b9da.otf">
   -->
 
-  <style>
-    @font-face {
-      font-family: "Inter-Regular";
-      src: local("Inter-Regular"), url(/static/media/Inter-Regular.1f5ed03b6dd9fd1f9982.otf) format("font/otf");
-      font-weight: 400;
-      font-style: normal;
-      font-display: swap;
-    }
-    @font-face {
-      font-family: "Inter-Italic";
-      src: local("Inter-Italic"), url(/static/media/Inter-Italic.95778eb0c75dc956257e.otf) format("font/otf");
-      font-weight: 400;
-      font-style: italic;
-      font-display: swap;
-    }
-    /*
-    @font-face {
-      font-family: "Inter-Medium";
-      src: local("Inter-Medium"), url(/static/media/Inter-Medium.296aa2d65964269836b3.otf) format("font/otf");
-      font-weight: 500;
-      font-style: normal;
-      font-display: swap;
-    }
-    @font-face {
-      font-family: "Inter-MediumItalic";
-      src: local("Inter-MediumItalic"), url(/static/media/Inter-MediumItalic.0e57e17a6311368e2114.otf) format("font/otf");
-      font-weight: 500;
-      font-style: italic;
-      font-display: swap;
-    }
-    */
-    @font-face {
-      font-family: "Inter-SemiBold";
-      src: local("Inter-SemiBold"), url(/static/media/Inter-SemiBold.2277990330981b8409bb.otf) format("font/otf");
-      font-weight: 600;
-      font-style: normal;
-      font-display: swap;
-    }
-    @font-face {
-      font-family: "Inter-SemiBoldItalic";
-      src: local("Inter-SemiBoldItalic"), url(/static/media/Inter-SemiBoldItalic.f62fea3df3a521d6c8a7.otf) format("font/otf");
-      font-weight: 600;
-      font-style: italic;
-      font-display: swap;
-    }
-    /*
-    @font-face {
-      font-family: "Inter-Bold";
-      src: local("Inter-Bold"), url(/static/media/Inter-Bold.8d330503e1d034ad68de.otf) format("font/otf");
-      font-weight: 700;
-      font-style: normal;
-      font-display: swap;
-    }
-    @font-face {
-      font-family: "Inter-BoldItalic";
-      src: local("Inter-BoldItalic"), url(/static/media/Inter-BoldItalic.bb17e63f9baa0d861a20.otf) format("font/otf");
-      font-weight: 700;
-      font-style: italic;
-      font-display: swap;
-    }
-    */
-    @font-face {
-      font-family: "Inter-ExtraBold";
-      src: local("Inter-ExtraBold"), url(/static/media/Inter-ExtraBold.ff2581a193bf6b7e0b06.otf) format("font/otf");
-      font-weight: 800;
-      font-style: normal;
-      font-display: swap;
-    }
-    @font-face {
-      font-family: "Inter-ExtraBoldItalic";
-      src: local("Inter-ExtraBoldItalic"), url(/static/media/Inter-ExtraBoldItalic.0e50b40728d24d40fdf4.otf) format("font/otf");
-      font-weight: 800;
-      font-style: italic;
-      font-display: swap;
-    }
-    /*
-    @font-face {
-      font-family: "Inter-Black";
-      src: local("Inter-Black"), url(/static/media/Inter-Black.66e9a87f1c921e844ed4.otf) format("font/otf");
-      font-weight: 900;
-      font-style: normal;
-      font-display: swap;
-    }
-    @font-face {
-      font-family: "Inter-BlackItalic";
-      src: local("Inter-BlackItalic"), url(/static/media/Inter-BlackItalic.27b9f0ad06fd13a7b9da.otf) format("font/otf");
-      font-weight: 900;
-      font-style: italic;
-      font-display: swap;
-    }
-    */
-
-    /**
-     * Extend the react-native-web reset:
-     * https://github.com/necolas/react-native-web/blob/master/packages/react-native-web/src/exports/StyleSheet/initialRules.js
-     */
-    html,
-    body,
-    #root {
-      width: 100%;
-      /* To smooth any scrolling behavior */
-      -webkit-overflow-scrolling: touch;
-      margin: 0px;
-      padding: 0px;
-      /* Allows content to fill the viewport and go beyond the bottom */
-      min-height: 100%;
-    }
-    #root {
-      flex-shrink: 0;
-      flex-basis: auto;
-      flex-grow: 1;
-      display: flex;
-      flex: 1;
-    }
-
-    html {
-      /* Prevent text size change on orientation change https://gist.github.com/tfausak/2222823#file-ios-8-web-app-html-L138 */
-      -webkit-text-size-adjust: 100%;
-      height: calc(100% + env(safe-area-inset-top));
-      scrollbar-gutter: stable both-edges;
-    }
-    html, body {
-      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Liberation Sans", Helvetica, Arial, sans-serif;
-    }
-
-    #preload {
-      width: 100px;
-      position: fixed;
-      left: 50%;
-      top: 50%;
-      transform: translate(-50%, -50%);
-    }
-
-    /* Buttons and inputs have a font set by UA, so we'll have to reset that */
-    button, input, textarea {
-      font: inherit;
-      line-height: inherit;
-    }
-
-    /* Color theming */
-    /* Default will always be white */
-    :root {
-        --text: black;
-        --background: white;
-        --backgroundLight: hsl(211, 20%, 95%);
-    }
-    /* This gives us a black background when system is dark and we have not loaded the theme/color scheme values in JS */
-    @media (prefers-color-scheme: dark) {
-        :root {
-            --text: white;
-            --background: black;
-            --backgroundLight: hsl(211, 20%, 20%);
-            color-scheme: dark;
-        }
-    }
-
-    /* Overwrite those preferences with the selected theme */
-    html.theme--light {
-        --text: black;
-        --background: white;
-        --backgroundLight: hsl(211, 20%, 95%);
-    }
-    html.theme--dark {
-        --text: white;
-        --background: black;
-        --backgroundLight: hsl(211, 20%, 20%);
-        color-scheme: dark;
-    }
-    html.theme--dim {
-        --text: white;
-        --background: hsl(211, 20%, 4%);
-        --backgroundLight: hsl(211, 20%, 10%);
-        color-scheme: dark;
-    }
-
-    /* Remove autofill styles on Webkit */
-    input:autofill,
-    input:-webkit-autofill,
-    input:-webkit-autofill:hover,
-    input:-webkit-autofill:focus,
-    input:-webkit-autofill:active{
-        -webkit-background-clip: text;
-        -webkit-text-fill-color: var(--text);
-        transition: background-color 5000s ease-in-out 0s;
-        box-shadow: inset 0 0 20px 20px var(--background);
-        background: var(--background);
-        color: var(--text);
-    }
-    /* Force left-align date/time inputs on iOS mobile */
-    input::-webkit-date-and-time-value {
-      text-align: left;
-    }
-
-    body {
-      display: flex;
-      /* Allows you to scroll below the viewport; default value is visible */
-      overflow-y: auto;
-      overscroll-behavior-y: none;
-      text-rendering: optimizeLegibility;
-      background-color: var(--background);
-      -webkit-font-smoothing: antialiased;
-      -moz-osx-font-smoothing: grayscale;
-      -ms-overflow-style: scrollbar;
-      font-synthesis-weight: none;
-    }
-
-    /* Remove default link styling */
-    a {
-      color: inherit;
-    }
-    a[role="link"]:hover {
-      text-decoration: underline;
-    }
-    a[role="link"][data-no-underline="1"]:hover {
-      text-decoration: none;
-    }
-
-    /* Styling hacks */
-    *[data-word-wrap] {
-      word-break: break-word;
-    }
-    *[data-stable-gutters] {
-      scrollbar-gutter: stable both-edges;
-    }
-
-    /* ProseMirror */
-    .ProseMirror {
-      min-height: 140px;
-    }
-    .ProseMirror-dark {
-      color: white;
-    }
-    .ProseMirror p {
-      margin: 0;
-    }
-    .ProseMirror p.is-editor-empty:first-child::before {
-      color: #8d8e96;
-      content: attr(data-placeholder);
-      float: left;
-      height: 0;
-      pointer-events: none;
-    }
-    .ProseMirror .mention {
-      color: #0085ff;
-    }
-    .ProseMirror a,
-    .ProseMirror .autolink {
-      color: #0085ff;
-    }
-    /* OLLIE: TODO -- this is not accessible */
-    /* Remove focus state on inputs */
-    .ProseMirror-focused {
-      outline: 0;
-    }
-    textarea:focus,
-    input:focus {
-      outline: 0;
-    }
-    .tippy-content .items {
-      width: fit-content;
-    }
-
-    /* Tooltips */
-    [data-tooltip] {
-      position: relative;
-      z-index: 10;
-    }
-    [data-tooltip]::after {
-      content: attr(data-tooltip);
-      display: none;
-      position: absolute;
-      bottom: 0;
-      left: 50%;
-      transform: translateY(100%) translateY(8px) translateX(-50%);
-      padding: 4px 10px;
-      border-radius: 10px;
-      background: var(--backgroundLight);
-      color: var(--text);
-      text-align: center;
-      white-space: nowrap;
-      font-size: 12px;
-      z-index: 10;
-    }
-    [data-tooltip]::before {
-      content: '';
-      display: none;
-      position: absolute;
-      border-bottom: 6px solid var(--backgroundLight);
-      border-left: 6px solid transparent;
-      border-right: 6px solid transparent;
-      bottom: 0;
-      left: 50%;
-      transform: translateY(100%) translateY(2px) translateX(-50%);
-      z-index: 10;
-    }
-    [data-tooltip]:hover::after,
-    [data-tooltip]:hover::before {
-      display:block;
-    }
-
-    /* NativeDropdown component */
-    .radix-dropdown-item:focus,
-    .nativeDropdown-item:focus {
-      outline: none;
-    }
-
-    /* Spinner component */
-    @keyframes rotate {
-      0% {
-        transform: rotate(0deg);
-      }
-      100% {
-        transform: rotate(360deg);
-      }
-    }
-    .rotate-500ms {
-      position: absolute;
-      inset:0;
-      animation: rotate 500ms linear infinite;
-    }
-
-    @keyframes avatarHoverFadeIn {
-      from { opacity: 0; }
-      to { opacity: 1; }
-    }
-
-    @keyframes avatarHoverFadeOut {
-      from { opacity: 1; }
-      to { opacity: 0; }
-    }
-
-    .force-no-clicks > *,
-    .force-no-clicks * {
-      pointer-events: none !important;
-    }
-
-    input[type=range][orient=vertical] {
-      writing-mode: vertical-lr;
-      direction: rtl;
-      appearance: slider-vertical;
-      width: 16px;
-      vertical-align: bottom;
-      -webkit-appearance: none;
-      appearance: none;
-      background: transparent;
-      cursor: pointer;
-    }
-
-    input[type="range"][orient=vertical]::-webkit-slider-runnable-track {
-      background: white;
-      height: 100%;
-      width: 4px;
-      border-radius: 4px;
-    }
-
-    input[type="range"][orient=vertical]::-moz-range-track {
-      background: white;
-      height: 100%;
-      width: 4px;
-      border-radius: 4px;
-    }
-
-    input[type="range"]::-webkit-slider-thumb {
-      -webkit-appearance: none;
-      appearance: none;
-      border-radius: 50%;
-      background-color: white;
-      height: 16px;
-      width: 16px;
-      margin-left: -6px;
-    }
-
-    input[type="range"][orient=vertical]::-moz-range-thumb {
-      border: none;
-      border-radius: 50%;
-      background-color: white;
-      height: 16px;
-      width: 16px;
-      margin-left: -6px;
-    }
-  </style>
   {% include "scripts.html" %}
   <link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon.png">
   <link rel="icon" type="image/png" sizes="32x32" href="/static/favicon-32x32.png">
diff --git a/package.json b/package.json
index 05b5f086d..e3aff1fe0 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
     "web": "expo start --web",
     "use-build-number": "./scripts/useBuildNumberEnv.sh",
     "use-build-number-with-bump": "./scripts/useBuildNumberEnvWithBump.sh",
-    "build-web": "expo export:web && node ./scripts/post-web-build.js && cp -v ./web-build/static/js/*.* ./bskyweb/static/js/ && cp -v ./web-build/static/media/* ./bskyweb/static/media/",
+    "build-web": "expo export:web && node ./scripts/post-web-build.js",
     "build-all": "yarn intl:build && yarn use-build-number-with-bump eas build --platform all",
     "build-ios": "yarn use-build-number-with-bump eas build -p ios",
     "build-android": "yarn use-build-number-with-bump eas build -p android",
diff --git a/scripts/post-web-build.js b/scripts/post-web-build.js
index baaa7cb8b..7bbee3855 100644
--- a/scripts/post-web-build.js
+++ b/scripts/post-web-build.js
@@ -20,7 +20,30 @@ console.log(`Writing ${templateFile}`)
 const outputFile = entrypoints
   .map(name => {
     const file = path.basename(name)
-    return `<script defer="defer" src="/static/js/${file}"></script>`
+    const ext = path.extname(file)
+
+    if (ext === '.js') {
+      return `<script defer="defer" src="/static/js/${file}"></script>`
+    }
+    if (ext === '.css') {
+      return `<link rel="stylesheet" href="/static/css/${file}">`
+    }
+
+    return ''
   })
   .join('\n')
 fs.writeFileSync(templateFile, outputFile)
+
+function copyFiles(sourceDir, targetDir) {
+  const files = fs.readdirSync(path.join(projectRoot, sourceDir))
+  files.forEach(file => {
+    const sourcePath = path.join(projectRoot, sourceDir, file)
+    const targetPath = path.join(projectRoot, targetDir, file)
+    fs.copyFileSync(sourcePath, targetPath)
+    console.log(`Copied ${sourcePath} to ${targetPath}`)
+  })
+}
+
+copyFiles('web-build/static/js', 'bskyweb/static/js')
+copyFiles('web-build/static/css', 'bskyweb/static/css')
+copyFiles('web-build/static/media', 'bskyweb/static/media')
diff --git a/src/App.web.tsx b/src/App.web.tsx
index c81ed10d3..7d98737a3 100644
--- a/src/App.web.tsx
+++ b/src/App.web.tsx
@@ -1,5 +1,6 @@
 import 'lib/sentry' // must be near top
 import 'view/icons'
+import './style.css'
 
 import React, {useEffect, useState} from 'react'
 import {KeyboardProvider} from 'react-native-keyboard-controller'
diff --git a/src/style.css b/src/style.css
new file mode 100644
index 000000000..29e9770e3
--- /dev/null
+++ b/src/style.css
@@ -0,0 +1,403 @@
+@font-face {
+  font-family: 'Inter-Regular';
+  src: local('Inter-Regular'),
+    url(/static/media/Inter-Regular.1f5ed03b6dd9fd1f9982.otf) format('font/otf');
+  font-weight: 400;
+  font-style: normal;
+  font-display: swap;
+}
+@font-face {
+  font-family: 'Inter-Italic';
+  src: local('Inter-Italic'),
+    url(/static/media/Inter-Italic.95778eb0c75dc956257e.otf) format('font/otf');
+  font-weight: 400;
+  font-style: italic;
+  font-display: swap;
+}
+/*
+@font-face {
+  font-family: "Inter-Medium";
+  src: local("Inter-Medium"), url(/static/media/Inter-Medium.296aa2d65964269836b3.otf) format("font/otf");
+  font-weight: 500;
+  font-style: normal;
+  font-display: swap;
+}
+@font-face {
+  font-family: "Inter-MediumItalic";
+  src: local("Inter-MediumItalic"), url(/static/media/Inter-MediumItalic.0e57e17a6311368e2114.otf) format("font/otf");
+  font-weight: 500;
+  font-style: italic;
+  font-display: swap;
+}
+*/
+@font-face {
+  font-family: 'Inter-SemiBold';
+  src: local('Inter-SemiBold'),
+    url(/static/media/Inter-SemiBold.2277990330981b8409bb.otf)
+      format('font/otf');
+  font-weight: 600;
+  font-style: normal;
+  font-display: swap;
+}
+@font-face {
+  font-family: 'Inter-SemiBoldItalic';
+  src: local('Inter-SemiBoldItalic'),
+    url(/static/media/Inter-SemiBoldItalic.f62fea3df3a521d6c8a7.otf)
+      format('font/otf');
+  font-weight: 600;
+  font-style: italic;
+  font-display: swap;
+}
+/*
+@font-face {
+  font-family: "Inter-Bold";
+  src: local("Inter-Bold"), url(/static/media/Inter-Bold.8d330503e1d034ad68de.otf) format("font/otf");
+  font-weight: 700;
+  font-style: normal;
+  font-display: swap;
+}
+@font-face {
+  font-family: "Inter-BoldItalic";
+  src: local("Inter-BoldItalic"), url(/static/media/Inter-BoldItalic.bb17e63f9baa0d861a20.otf) format("font/otf");
+  font-weight: 700;
+  font-style: italic;
+  font-display: swap;
+}
+*/
+@font-face {
+  font-family: 'Inter-ExtraBold';
+  src: local('Inter-ExtraBold'),
+    url(/static/media/Inter-ExtraBold.ff2581a193bf6b7e0b06.otf)
+      format('font/otf');
+  font-weight: 800;
+  font-style: normal;
+  font-display: swap;
+}
+@font-face {
+  font-family: 'Inter-ExtraBoldItalic';
+  src: local('Inter-ExtraBoldItalic'),
+    url(/static/media/Inter-ExtraBoldItalic.0e50b40728d24d40fdf4.otf)
+      format('font/otf');
+  font-weight: 800;
+  font-style: italic;
+  font-display: swap;
+}
+/*
+@font-face {
+  font-family: "Inter-Black";
+  src: local("Inter-Black"), url(/static/media/Inter-Black.66e9a87f1c921e844ed4.otf) format("font/otf");
+  font-weight: 900;
+  font-style: normal;
+  font-display: swap;
+}
+@font-face {
+  font-family: "Inter-BlackItalic";
+  src: local("Inter-BlackItalic"), url(/static/media/Inter-BlackItalic.27b9f0ad06fd13a7b9da.otf) format("font/otf");
+  font-weight: 900;
+  font-style: italic;
+  font-display: swap;
+}
+*/
+
+/**
+ * Extend the react-native-web reset:
+ * https://github.com/necolas/react-native-web/blob/master/packages/react-native-web/src/exports/StyleSheet/initialRules.js
+ */
+html,
+body,
+#root {
+  width: 100%;
+  /* To smooth any scrolling behavior */
+  -webkit-overflow-scrolling: touch;
+  margin: 0px;
+  padding: 0px;
+  /* Allows content to fill the viewport and go beyond the bottom */
+  min-height: 100%;
+}
+#root {
+  flex-shrink: 0;
+  flex-basis: auto;
+  flex-grow: 1;
+  display: flex;
+  flex: 1;
+}
+
+html {
+  /* Prevent text size change on orientation change https://gist.github.com/tfausak/2222823#file-ios-8-web-app-html-L138 */
+  -webkit-text-size-adjust: 100%;
+  height: calc(100% + env(safe-area-inset-top));
+  scrollbar-gutter: stable both-edges;
+}
+html,
+body {
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
+    'Liberation Sans', Helvetica, Arial, sans-serif;
+}
+
+#preload {
+  width: 100px;
+  position: fixed;
+  left: 50%;
+  top: 50%;
+  transform: translate(-50%, -50%);
+}
+
+/* Buttons and inputs have a font set by UA, so we'll have to reset that */
+button,
+input,
+textarea {
+  font: inherit;
+  line-height: inherit;
+}
+
+/* Color theming */
+/* Default will always be white */
+:root {
+  --text: black;
+  --background: white;
+  --backgroundLight: hsl(211, 20%, 95%);
+}
+/* This gives us a black background when system is dark and we have not loaded the theme/color scheme values in JS */
+@media (prefers-color-scheme: dark) {
+  :root {
+    --text: white;
+    --background: black;
+    --backgroundLight: hsl(211, 20%, 20%);
+    color-scheme: dark;
+  }
+}
+
+/* Overwrite those preferences with the selected theme */
+html.theme--light {
+  --text: black;
+  --background: white;
+  --backgroundLight: hsl(211, 20%, 95%);
+}
+html.theme--dark {
+  --text: white;
+  --background: black;
+  --backgroundLight: hsl(211, 20%, 20%);
+  color-scheme: dark;
+}
+html.theme--dim {
+  --text: white;
+  --background: hsl(211, 20%, 4%);
+  --backgroundLight: hsl(211, 20%, 10%);
+  color-scheme: dark;
+}
+
+/* Remove autofill styles on Webkit */
+input:autofill,
+input:-webkit-autofill,
+input:-webkit-autofill:hover,
+input:-webkit-autofill:focus,
+input:-webkit-autofill:active {
+  -webkit-background-clip: text;
+  -webkit-text-fill-color: var(--text);
+  transition: background-color 5000s ease-in-out 0s;
+  box-shadow: inset 0 0 20px 20px var(--background);
+  background: var(--background);
+  color: var(--text);
+}
+/* Force left-align date/time inputs on iOS mobile */
+input::-webkit-date-and-time-value {
+  text-align: left;
+}
+
+body {
+  display: flex;
+  /* Allows you to scroll below the viewport; default value is visible */
+  overflow-y: auto;
+  overscroll-behavior-y: none;
+  text-rendering: optimizeLegibility;
+  background-color: var(--background);
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  -ms-overflow-style: scrollbar;
+  font-synthesis-weight: none;
+}
+
+/* Remove default link styling */
+a {
+  color: inherit;
+}
+a[role='link']:hover {
+  text-decoration: underline;
+}
+a[role='link'][data-no-underline='1']:hover {
+  text-decoration: none;
+}
+
+/* Styling hacks */
+*[data-word-wrap] {
+  word-break: break-word;
+}
+*[data-stable-gutters] {
+  scrollbar-gutter: stable both-edges;
+}
+
+/* ProseMirror */
+.ProseMirror {
+  font: 18px -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
+    'Liberation Sans', Helvetica, Arial, sans-serif;
+  min-height: 140px;
+}
+.ProseMirror-dark {
+  color: white;
+}
+.ProseMirror p {
+  margin: 0;
+}
+.ProseMirror p.is-editor-empty:first-child::before {
+  color: #8d8e96;
+  content: attr(data-placeholder);
+  float: left;
+  height: 0;
+  pointer-events: none;
+}
+.ProseMirror .mention {
+  color: #0085ff;
+}
+.ProseMirror a,
+.ProseMirror .autolink {
+  color: #0085ff;
+}
+/* OLLIE: TODO -- this is not accessible */
+/* Remove focus state on inputs */
+.ProseMirror-focused {
+  outline: 0;
+}
+textarea:focus,
+input:focus {
+  outline: 0;
+}
+.tippy-content .items {
+  width: fit-content;
+}
+
+/* Tooltips */
+[data-tooltip] {
+  position: relative;
+  z-index: 10;
+}
+[data-tooltip]::after {
+  content: attr(data-tooltip);
+  display: none;
+  position: absolute;
+  bottom: 0;
+  left: 50%;
+  transform: translateY(100%) translateY(8px) translateX(-50%);
+  padding: 4px 10px;
+  border-radius: 10px;
+  background: var(--backgroundLight);
+  color: var(--text);
+  text-align: center;
+  white-space: nowrap;
+  font-size: 12px;
+  z-index: 10;
+}
+[data-tooltip]::before {
+  content: '';
+  display: none;
+  position: absolute;
+  border-bottom: 6px solid var(--backgroundLight);
+  border-left: 6px solid transparent;
+  border-right: 6px solid transparent;
+  bottom: 0;
+  left: 50%;
+  transform: translateY(100%) translateY(2px) translateX(-50%);
+  z-index: 10;
+}
+[data-tooltip]:hover::after,
+[data-tooltip]:hover::before {
+  display: block;
+}
+
+/* NativeDropdown component */
+.radix-dropdown-item:focus,
+.nativeDropdown-item:focus {
+  outline: none;
+}
+
+/* Spinner component */
+@keyframes rotate {
+  0% {
+    transform: rotate(0deg);
+  }
+  100% {
+    transform: rotate(360deg);
+  }
+}
+.rotate-500ms {
+  position: absolute;
+  inset: 0;
+  animation: rotate 500ms linear infinite;
+}
+
+@keyframes avatarHoverFadeIn {
+  from {
+    opacity: 0;
+  }
+  to {
+    opacity: 1;
+  }
+}
+
+@keyframes avatarHoverFadeOut {
+  from {
+    opacity: 1;
+  }
+  to {
+    opacity: 0;
+  }
+}
+
+.force-no-clicks > *,
+.force-no-clicks * {
+  pointer-events: none !important;
+}
+
+input[type='range'][orient='vertical'] {
+  writing-mode: vertical-lr;
+  direction: rtl;
+  appearance: slider-vertical;
+  width: 16px;
+  vertical-align: bottom;
+  -webkit-appearance: none;
+  appearance: none;
+  background: transparent;
+  cursor: pointer;
+}
+
+input[type='range'][orient='vertical']::-webkit-slider-runnable-track {
+  background: white;
+  height: 100%;
+  width: 4px;
+  border-radius: 4px;
+}
+
+input[type='range'][orient='vertical']::-moz-range-track {
+  background: white;
+  height: 100%;
+  width: 4px;
+  border-radius: 4px;
+}
+
+input[type='range']::-webkit-slider-thumb {
+  -webkit-appearance: none;
+  appearance: none;
+  border-radius: 50%;
+  background-color: white;
+  height: 16px;
+  width: 16px;
+  margin-left: -6px;
+}
+
+input[type='range'][orient='vertical']::-moz-range-thumb {
+  border: none;
+  border-radius: 50%;
+  background-color: white;
+  height: 16px;
+  width: 16px;
+  margin-left: -6px;
+}
diff --git a/web/index.html b/web/index.html
index 3a132d25b..512178327 100644
--- a/web/index.html
+++ b/web/index.html
@@ -36,388 +36,6 @@
     <link rel="preload" as="font" type="font/otf" href="/static/media/Inter-Black.66e9a87f1c921e844ed4.otf">
     <link rel="preload" as="font" type="font/otf" href="/static/media/Inter-BlackItalic.27b9f0ad06fd13a7b9da.otf">
     -->
-
-    <style>
-      @font-face {
-        font-family: "Inter-Regular";
-        src: local("Inter-Regular"), url(/static/media/Inter-Regular.1f5ed03b6dd9fd1f9982.otf) format("font/otf");
-        font-weight: 400;
-        font-style: normal;
-        font-display: swap;
-      }
-      @font-face {
-        font-family: "Inter-Italic";
-        src: local("Inter-Italic"), url(/static/media/Inter-Italic.95778eb0c75dc956257e.otf) format("font/otf");
-        font-weight: 400;
-        font-style: italic;
-        font-display: swap;
-      }
-      /*
-      @font-face {
-        font-family: "Inter-Medium";
-        src: local("Inter-Medium"), url(/static/media/Inter-Medium.296aa2d65964269836b3.otf) format("font/otf");
-        font-weight: 500;
-        font-style: normal;
-        font-display: swap;
-      }
-      @font-face {
-        font-family: "Inter-MediumItalic";
-        src: local("Inter-MediumItalic"), url(/static/media/Inter-MediumItalic.0e57e17a6311368e2114.otf) format("font/otf");
-        font-weight: 500;
-        font-style: italic;
-        font-display: swap;
-      }
-      */
-      @font-face {
-        font-family: "Inter-SemiBold";
-        src: local("Inter-SemiBold"), url(/static/media/Inter-SemiBold.2277990330981b8409bb.otf) format("font/otf");
-        font-weight: 600;
-        font-style: normal;
-        font-display: swap;
-      }
-      @font-face {
-        font-family: "Inter-SemiBoldItalic";
-        src: local("Inter-SemiBoldItalic"), url(/static/media/Inter-SemiBoldItalic.f62fea3df3a521d6c8a7.otf) format("font/otf");
-        font-weight: 600;
-        font-style: italic;
-        font-display: swap;
-      }
-      /*
-      @font-face {
-        font-family: "Inter-Bold";
-        src: local("Inter-Bold"), url(/static/media/Inter-Bold.8d330503e1d034ad68de.otf) format("font/otf");
-        font-weight: 700;
-        font-style: normal;
-        font-display: swap;
-      }
-      @font-face {
-        font-family: "Inter-BoldItalic";
-        src: local("Inter-BoldItalic"), url(/static/media/Inter-BoldItalic.bb17e63f9baa0d861a20.otf) format("font/otf");
-        font-weight: 700;
-        font-style: italic;
-        font-display: swap;
-      }
-      */
-      @font-face {
-        font-family: "Inter-ExtraBold";
-        src: local("Inter-ExtraBold"), url(/static/media/Inter-ExtraBold.ff2581a193bf6b7e0b06.otf) format("font/otf");
-        font-weight: 800;
-        font-style: normal;
-        font-display: swap;
-      }
-      @font-face {
-        font-family: "Inter-ExtraBoldItalic";
-        src: local("Inter-ExtraBoldItalic"), url(/static/media/Inter-ExtraBoldItalic.0e50b40728d24d40fdf4.otf) format("font/otf");
-        font-weight: 800;
-        font-style: italic;
-        font-display: swap;
-      }
-      /*
-      @font-face {
-        font-family: "Inter-Black";
-        src: local("Inter-Black"), url(/static/media/Inter-Black.66e9a87f1c921e844ed4.otf) format("font/otf");
-        font-weight: 900;
-        font-style: normal;
-        font-display: swap;
-      }
-      @font-face {
-        font-family: "Inter-BlackItalic";
-        src: local("Inter-BlackItalic"), url(/static/media/Inter-BlackItalic.27b9f0ad06fd13a7b9da.otf) format("font/otf");
-        font-weight: 900;
-        font-style: italic;
-        font-display: swap;
-      }
-      */
-
-      /**
-       * Extend the react-native-web reset:
-       * https://github.com/necolas/react-native-web/blob/master/packages/react-native-web/src/exports/StyleSheet/initialRules.js
-       */
-      html,
-      body,
-      #root {
-        width: 100%;
-        /* To smooth any scrolling behavior */
-        -webkit-overflow-scrolling: touch;
-        margin: 0px;
-        padding: 0px;
-        /* Allows content to fill the viewport and go beyond the bottom */
-        min-height: 100%;
-      }
-      #root {
-        flex-shrink: 0;
-        flex-basis: auto;
-        flex-grow: 1;
-        display: flex;
-        flex: 1;
-      }
-
-      html {
-        /* Prevent text size change on orientation change https://gist.github.com/tfausak/2222823#file-ios-8-web-app-html-L138 */
-        -webkit-text-size-adjust: 100%;
-        height: calc(100% + env(safe-area-inset-top));
-        scrollbar-gutter: stable both-edges;
-      }
-      html, body {
-        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Liberation Sans", Helvetica, Arial, sans-serif;
-      }
-
-      #preload {
-        width: 100px;
-        position: fixed;
-        left: 50%;
-        top: 50%;
-        transform: translate(-50%, -50%);
-      }
-
-      /* Buttons and inputs have a font set by UA, so we'll have to reset that */
-      button, input, textarea {
-        font: inherit;
-        line-height: inherit;
-      }
-
-      /* Color theming */
-      /* Default will always be white */
-      :root {
-          --text: black;
-          --background: white;
-          --backgroundLight: hsl(211, 20%, 95%);
-      }
-      /* This gives us a black background when system is dark and we have not loaded the theme/color scheme values in JS */
-      @media (prefers-color-scheme: dark) {
-          :root {
-              --text: white;
-              --background: black;
-              --backgroundLight: hsl(211, 20%, 20%);
-              color-scheme: dark;
-          }
-      }
-
-      /* Overwrite those preferences with the selected theme */
-      html.theme--light {
-          --text: black;
-          --background: white;
-          --backgroundLight: hsl(211, 20%, 95%);
-      }
-      html.theme--dark {
-          --text: white;
-          --background: black;
-          --backgroundLight: hsl(211, 20%, 20%);
-          color-scheme: dark;
-      }
-      html.theme--dim {
-          --text: white;
-          --background: hsl(211, 20%, 4%);
-          --backgroundLight: hsl(211, 20%, 10%);
-          color-scheme: dark;
-      }
-
-      /* Remove autofill styles on Webkit */
-      input:autofill,
-      input:-webkit-autofill,
-      input:-webkit-autofill:hover,
-      input:-webkit-autofill:focus,
-      input:-webkit-autofill:active{
-          -webkit-background-clip: text;
-          -webkit-text-fill-color: var(--text);
-          transition: background-color 5000s ease-in-out 0s;
-          box-shadow: inset 0 0 20px 20px var(--background);
-          background: var(--background);
-          color: var(--text);
-      }
-      /* Force left-align date/time inputs on iOS mobile */
-      input::-webkit-date-and-time-value {
-        text-align: left;
-      }
-
-      body {
-        display: flex;
-        /* Allows you to scroll below the viewport; default value is visible */
-        overflow-y: auto;
-        overscroll-behavior-y: none;
-        text-rendering: optimizeLegibility;
-        background-color: var(--background);
-        -webkit-font-smoothing: antialiased;
-        -moz-osx-font-smoothing: grayscale;
-        -ms-overflow-style: scrollbar;
-        font-synthesis-weight: none;
-      }
-
-      /* Remove default link styling */
-      a {
-        color: inherit;
-      }
-      a[role="link"]:hover {
-        text-decoration: underline;
-      }
-      a[role="link"][data-no-underline="1"]:hover {
-        text-decoration: none;
-      }
-
-      /* Styling hacks */
-      *[data-word-wrap] {
-        word-break: break-word;
-      }
-      *[data-stable-gutters] {
-        scrollbar-gutter: stable both-edges;
-      }
-
-      /* ProseMirror */
-      .ProseMirror {
-        min-height: 140px;
-      }
-      .ProseMirror-dark {
-        color: white;
-      }
-      .ProseMirror p {
-        margin: 0;
-      }
-      .ProseMirror p.is-editor-empty:first-child::before {
-        color: #8d8e96;
-        content: attr(data-placeholder);
-        float: left;
-        height: 0;
-        pointer-events: none;
-      }
-      .ProseMirror .mention {
-        color: #0085ff;
-      }
-      .ProseMirror a,
-      .ProseMirror .autolink {
-        color: #0085ff;
-      }
-      /* OLLIE: TODO -- this is not accessible */
-      /* Remove focus state on inputs */
-      .ProseMirror-focused {
-        outline: 0;
-      }
-      textarea:focus,
-      input:focus {
-        outline: 0;
-      }
-      .tippy-content .items {
-        width: fit-content;
-      }
-
-      /* Tooltips */
-      [data-tooltip] {
-        position: relative;
-        z-index: 10;
-      }
-      [data-tooltip]::after {
-        content: attr(data-tooltip);
-        display: none;
-        position: absolute;
-        bottom: 0;
-        left: 50%;
-        transform: translateY(100%) translateY(8px) translateX(-50%);
-        padding: 4px 10px;
-        border-radius: 10px;
-        background: var(--backgroundLight);
-        color: var(--text);
-        text-align: center;
-        white-space: nowrap;
-        font-size: 12px;
-        z-index: 10;
-      }
-      [data-tooltip]::before {
-        content: '';
-        display: none;
-        position: absolute;
-        border-bottom: 6px solid var(--backgroundLight);
-        border-left: 6px solid transparent;
-        border-right: 6px solid transparent;
-        bottom: 0;
-        left: 50%;
-        transform: translateY(100%) translateY(2px) translateX(-50%);
-        z-index: 10;
-      }
-      [data-tooltip]:hover::after,
-      [data-tooltip]:hover::before {
-        display:block;
-      }
-
-      /* NativeDropdown component */
-      .radix-dropdown-item:focus,
-      .nativeDropdown-item:focus {
-        outline: none;
-      }
-
-      /* Spinner component */
-      @keyframes rotate {
-        0% {
-          transform: rotate(0deg);
-        }
-        100% {
-          transform: rotate(360deg);
-        }
-      }
-      .rotate-500ms {
-        position: absolute;
-        inset:0;
-        animation: rotate 500ms linear infinite;
-      }
-
-      @keyframes avatarHoverFadeIn {
-        from { opacity: 0; }
-        to { opacity: 1; }
-      }
-
-      @keyframes avatarHoverFadeOut {
-        from { opacity: 1; }
-        to { opacity: 0; }
-      }
-
-      .force-no-clicks > *,
-      .force-no-clicks * {
-        pointer-events: none !important;
-      }
-
-      input[type=range][orient=vertical] {
-        writing-mode: vertical-lr;
-        direction: rtl;
-        appearance: slider-vertical;
-        width: 16px;
-        vertical-align: bottom;
-        -webkit-appearance: none;
-        appearance: none;
-        background: transparent;
-        cursor: pointer;
-      }
-
-      input[type="range"][orient=vertical]::-webkit-slider-runnable-track {
-        background: white;
-        height: 100%;
-        width: 4px;
-        border-radius: 4px;
-      }
-
-      input[type="range"][orient=vertical]::-moz-range-track {
-        background: white;
-        height: 100%;
-        width: 4px;
-        border-radius: 4px;
-      }
-
-      input[type="range"]::-webkit-slider-thumb {
-        -webkit-appearance: none;
-        appearance: none;
-        border-radius: 50%;
-        background-color: white;
-        height: 16px;
-        width: 16px;
-        margin-left: -6px;
-      }
-
-      input[type="range"][orient=vertical]::-moz-range-thumb {
-        border: none;
-        border-radius: 50%;
-        background-color: white;
-        height: 16px;
-        width: 16px;
-        margin-left: -6px;
-      }
-    </style>
   </head>
 
   <body>