overhaul frontend, typography, noise pattern, badge, notify audio

This commit is contained in:
2024-12-22 08:51:15 -06:00
parent a0417e0b19
commit 6f7139d5d7
11 changed files with 604 additions and 41 deletions

View File

@@ -0,0 +1,48 @@
import { cn, type ClassValue } from "@/util";
import type { JSX } from "astro/jsx-runtime";
type BadgeProps = {
className?: ClassValue;
onClick?: () => void;
children: JSX.Element | JSX.Element[];
screenReaderLabel?: string;
};
const Badge = ({
className,
children,
onClick,
screenReaderLabel,
}: BadgeProps) => {
return (
<span
id="badge-dismiss-dark"
class={cn(
className,
"inline-flex items-center px-2 py-1 me-2 text-sm font-medium text-zinc-800 bg-zinc-100 rounded dark:bg-zinc-700 dark:text-zinc-300"
)}
>
{children}
<button
type="button"
onClick={onClick}
class="inline-flex items-center p-1 ms-1 text-sm text-zinc-400 bg-transparent rounded-sm hover:bg-zinc-200 hover:text-zinc-900 dark:hover:bg-zinc-600 dark:hover:text-zinc-300"
data-dismiss-target="#badge-dismiss-dark"
aria-label="Remove"
>
<svg class="w-2 h-2" aria-hidden="true" fill="none" viewBox="0 0 14 14">
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"
/>
</svg>
<span class="sr-only">{screenReaderLabel ?? "Remove"}</span>
</button>
</span>
);
};
export default Badge;

View File

@@ -0,0 +1,48 @@
import Badge from "@/components/Badge";
import { useState } from "preact/hooks";
type StatefulDemoProps = {
class?: string;
};
type SessionData = {
id: string;
downloads: string[];
};
const StatefulDemo = ({ class: className }: StatefulDemoProps) => {
const [session, setSession] = useState<SessionData>({
id: "0x59AF5",
downloads: ["0xABF4"],
});
return (
<div class="px-5 leading-6">
<p class="mt-3">
This demo uses websockets to communicate between the server and the
browser. Each download gets a unique identifier bound to the user
session.
<br />
Your session is{" "}
<b class="text-teal-400 font-inter">{session?.id ?? "loading"}</b>. You
have <b class="text-teal-400 font-inter">{session?.downloads.length}</b>{" "}
known downloads.
</p>
<div>
{session?.downloads.map((download) => (
<Badge
onClick={function onClick() {
const audio = new Audio("/notify.wav");
audio.volume = 0.3;
audio.play();
}}
>
{download}
</Badge>
))}
</div>
</div>
);
};
export default StatefulDemo;

81
frontend/src/globals.scss Normal file
View File

@@ -0,0 +1,81 @@
@tailwind base;
@import url("https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Source+Serif+4:ital,opsz,wght@0,8..60,200..900;1,8..60,200..900&display=swap");
.noise::after {
background-image: url("/noise.png");
position: fixed;
z-index: 10;
left: 0;
right: 0;
top: 0;
bottom: 0;
opacity: 50%;
content: " ";
pointer-events: none;
}
.noise-card {
@apply bg-zinc-800;
&::before {
position: absolute;
z-index: 30;
left: 0;
right: 0;
top: 0;
bottom: 0;
content: " ";
pointer-events: none;
background-image: url("/noise.png");
background-position-x: 66px;
// background-color: rgba(255, 255, 255, 6%);
opacity: 80%;
// filter: brightness(100%);
// background: linear-gradient(rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.2)), url("/noise.png");
// #202020;
}
}
html,
body {
@apply overflow-x-hidden;
background-color: #151413;
color: #bab1a8;
margin: 0;
width: 100%;
height: 100%;
}
@layer base {
* {
/* @apply border-border; */
}
body {
/* @apply bg-background text-foreground; */
}
ul,
ol {
list-style: revert;
}
/* NEW CODE */
/* width */
::-webkit-scrollbar {
@apply w-2 rounded-lg;
}
/* Track */
::-webkit-scrollbar-track {
@apply bg-zinc-700 rounded-lg;
}
/* Handle */
::-webkit-scrollbar-thumb {
@apply bg-zinc-500 rounded-xl;
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
@apply bg-zinc-400 rounded-lg;
}
}

View File

@@ -6,16 +6,7 @@
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<title>Dynamic Preauth</title>
</head>
<body>
<body class="noise after:opacity-70">
<slot />
</body>
</html>
<style>
html,
body {
margin: 0;
width: 100%;
height: 100%;
}
</style>

View File

@@ -1,6 +1,51 @@
---
// import Welcome from '@/components/Welcome.astro';
import Layout from "@/layouts/Base.astro";
import Base from "@/layouts/Base.astro";
import StatefulDemo from "@/components/StatefulDemo.tsx";
import "@/globals.scss";
---
<Layout />
<Base>
<div class="w-screen h-screen flex flex-col items-center middle">
<div
class="noise-card rounded-sm relative z-20 border-zinc-700 border w-full max-w-[40rem] mt-16 mb-8 shadow-lg"
style={{
backgroundPosition:
Math.random() * 100 + "px " + Math.random() * 100 + "px",
}}
>
<div class="px-3 pt-4 pb-5 font-sans prose prose-zinc prose-invert">
<h1
class="text-5xl font-bebas bold text-center mb-3 text-zinc-300"
style={{ textShadow: "0 0 10px rgba(0,0,0,0.7)" }}
>
Dynamic Preauthentication
</h1>
<div class="px-5 leading-6">
<p class="mt-1">
This is a proof of concept for what I'm calling <b
class="text-teal-500">Dynamic Preauthentication</b
>. Essentially, a precompiled executable keeps a constant time
string that has a known (or easily extracted) pattern.
</p>
<p class="mt-0">
When a download is requested, the server can generate and embed a
token that allows the application to authenticate the user
immediately.
</p>
<p class="mt-0">
This would allow users to skip the initial login process and
immediately start using the application. It could also be used to
track downloads, hint at user behaviors, or create unique user
experiences.
</p>
</div>
<div class="inline-flex items-center justify-center w-full">
<hr class="w-32 h-px border-0 bg-zinc-600 my-0" />
<span class="px-3 text-2xl font-bebas tracking-wide">Demo</span>
<hr class="w-32 h-px border-0 bg-zinc-600 my-0" />
</div>
<StatefulDemo client:load />
</div>
</div>
</div>
</Base>

7
frontend/src/util.ts Normal file
View File

@@ -0,0 +1,7 @@
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export { type ClassValue };
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}