diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json
index d01051b..814b36b 100644
--- a/npm-shrinkwrap.json
+++ b/npm-shrinkwrap.json
@@ -35,6 +35,7 @@
"p-defer": "^4.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "react-router-dom": "^6.14.2",
"webextension-polyfill": "^0.10.0"
},
"devDependencies": {
@@ -3109,6 +3110,14 @@
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
},
+ "node_modules/@remix-run/router": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.7.2.tgz",
+ "integrity": "sha512-7Lcn7IqGMV+vizMPoEl5F0XDshcdDYtMI6uJLQdQz5CfZAwy3vvGKYSUk789qndt5dEC4HfSjviSYlSoHGL2+A==",
+ "engines": {
+ "node": ">=14"
+ }
+ },
"node_modules/@rollup/plugin-inject": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/@rollup/plugin-inject/-/plugin-inject-5.0.3.tgz",
@@ -18425,6 +18434,36 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-router": {
+ "version": "6.14.2",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.14.2.tgz",
+ "integrity": "sha512-09Zss2dE2z+T1D03IheqAFtK4UzQyX8nFPWx6jkwdYzGLXd5ie06A6ezS2fO6zJfEb/SpG6UocN2O1hfD+2urQ==",
+ "dependencies": {
+ "@remix-run/router": "1.7.2"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "react": ">=16.8"
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "6.14.2",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.14.2.tgz",
+ "integrity": "sha512-5pWX0jdKR48XFZBuJqHosX3AAHjRAzygouMTyimnBPOLdY3WjzUSKhus2FVMihUFWzeLebDgr4r8UeQFAct7Bg==",
+ "dependencies": {
+ "@remix-run/router": "1.7.2",
+ "react-router": "6.14.2"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "react": ">=16.8",
+ "react-dom": ">=16.8"
+ }
+ },
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
diff --git a/package.json b/package.json
index 4a0e2ba..1379c9f 100644
--- a/package.json
+++ b/package.json
@@ -58,6 +58,7 @@
"p-defer": "^4.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "react-router-dom": "^6.14.2",
"webextension-polyfill": "^0.10.0"
},
"overrides": {
diff --git a/ui/apps/onboarding/App.scss b/ui/apps/onboarding/App.scss
new file mode 100644
index 0000000..86ac6f0
--- /dev/null
+++ b/ui/apps/onboarding/App.scss
@@ -0,0 +1,136 @@
+@import "../../styles/global";
+@import "../../styles/mixins";
+@import "../../styles/vars";
+
+main {
+ position: relative;
+ background: #080808;
+}
+
+.art,
+.content {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ width: 50%;
+}
+
+.art {
+ transition: opacity 250ms ease;
+ opacity: 0;
+
+ &.art-1.visible, &.art-2.visible, &.art-3.visible, &.art-4.visible {
+ opacity: 1;
+ }
+}
+
+.art-1 {
+ left: 0;
+ background-image: url("../../assets/onboarding-1.jpg");
+ background-position: 50%;
+ background-size: cover;
+ border-right: 1px solid #363636;
+}
+
+.art-2 {
+ left: 50%;
+ background-image: url("../../assets/onboarding-2.jpg");
+ background-position: 50%;
+ background-size: cover;
+ border-left: 1px solid #363636;
+}
+
+.art-3 {
+ left: 50%;
+ background-image: url("../../assets/onboarding-3.jpg");
+ background-position: 50%;
+ background-size: cover;
+ border-left: 1px solid #363636;
+}
+
+.art-4 {
+ left: 50%;
+ background-image: url("../../assets/onboarding-4.jpg");
+ background-position: 50%;
+ background-size: cover;
+ border-left: 1px solid #363636;
+}
+
+.content {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ left: 50%;
+ padding: 5.5em 3.75%;
+ color: #fff;
+ background: #080808;
+ transition: left 250ms cubic-bezier(0.34, 1.56, 0.64, 1);
+ overflow: auto;
+
+ > div:first-child {
+ flex-grow: 1;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ max-width: 43.2em;
+ }
+}
+
+.page {
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-end;
+ min-height: 42em;
+}
+
+h1 {
+ font-family: $font-family-jetbrains-mono;
+ @include fluid-font-size(3.125rem);
+ line-height: 110%;
+}
+
+p {
+ @include fluid-font-size(1.25rem);
+ margin-top: 1.4em;
+ line-height: 122%;
+ color: #808080;
+}
+
+.btn-wrapper {
+ margin-top: 5.5em;
+
+ a {
+ text-decoration: none;
+ color: inherit;
+ }
+}
+
+.grant-info {
+ @include fluid-font-size(1rem);
+ margin-top: 5em;
+ max-width: 27em;
+ line-height: 125%;
+ color: #808080;
+
+ a {
+ color: #fff;
+ }
+}
+
+.started {
+ .content {
+ left: 0;
+ }
+}
+
+@media screen and (max-width: 48rem) {
+ .art {
+ display: none;
+ }
+
+ .content {
+ left: 0;
+ width: 100%;
+ transition: none;
+ }
+}
diff --git a/ui/apps/onboarding/App.tsx b/ui/apps/onboarding/App.tsx
new file mode 100644
index 0000000..c4c1d39
--- /dev/null
+++ b/ui/apps/onboarding/App.tsx
@@ -0,0 +1,153 @@
+import {
+ createHashRouter,
+ RouteObject,
+ RouterProvider,
+ useMatches,
+} from "react-router-dom";
+import Page from "./components/Page.js";
+// @ts-ignore
+import lumeLogo from "../../assets/lume-logo.png";
+// @ts-ignore
+import classNames from "classnames";
+import "./App.scss";
+// @ts-ignore
+import browser from "webextension-polyfill";
+
+const contentPages = [
+ {
+ heading: <>Thank you for supporting an open web.>,
+ content: (
+ <>
+ We are an independent, pure organization. We have decided not to take
+ money from venture capitalists. Nor do we have a large treasury funding
+ our work.
+ >
+ ),
+ },
+ {
+ heading: <>Thank you for supporting an open web.>,
+ content: (
+ <>
+ Easy Access to Web3. With native Handshake (HNS) and Ethereum (ENS)
+ support, you can forget eth.link and hns.is. This is your gateway.
+ >
+ ),
+ },
+ {
+ heading: <>Thank you for supporting an open web.>,
+ content: (
+ <>
+ We are an independent, pure organization. We have decided not to take
+ money from venture capitalists. Nor do we have a large treasury funding
+ our work.
+ >
+ ),
+ },
+ {
+ heading: <>Thank you for supporting an open web.>,
+ content: (
+ <>
+ Stop worrying about being vendor-locked. Remain flexible and reduce your
+ storage costs by 50% or more. Lume is affordable storage on-demand.
+ >
+ ),
+ },
+] as {
+ heading: React.ReactElement;
+ content: React.ReactElement;
+}[];
+
+contentPages.map((page, index) => {
+ if (!index) {
+ return;
+ }
+});
+
+const childRoutes = contentPages.slice(1).map((item, index): RouteObject => {
+ return {
+ path: `page-${index + 1}`,
+ handle: {
+ page: index + 1,
+ },
+ };
+});
+
+const rootRoute = {
+ path: "/",
+ element: