summaryrefslogtreecommitdiff
path: root/src/routes/Header.svelte
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/routes/Header.svelte299
1 files changed, 299 insertions, 0 deletions
diff --git a/src/routes/Header.svelte b/src/routes/Header.svelte
new file mode 100644
index 0000000..0366e6d
--- /dev/null
+++ b/src/routes/Header.svelte
@@ -0,0 +1,299 @@
+<script context="module" lang="ts">
+ import { type ComponentType, SvelteComponent} from "svelte";
+ var showmsg: boolean = false;
+
+ export type Header = {
+ addMessage (message: string, options?: Partial<MessageOptions>): void
+ };
+
+ type HeaderMessage = {
+ message: string,
+ options: MessageOptions,
+ dismiss: () => void,
+ }
+
+ type MessageOptions = {
+ time: number,
+ dismissable: boolean,
+ };
+
+ var chrome: boolean = /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor);
+</script>
+
+<script lang="ts">
+ import { page } from '$app/stores';
+ import logo from '/rose.gif';
+ import boowomp from '/wilted-flower.gif';
+ import close from '$lib/assets/close.svg?raw';
+ import { setContext } from "svelte";
+ import { fly } from "svelte/transition";
+ import { readable, writable } from "svelte/store";
+
+ const newestMessage = writable<HeaderMessage|null>(null);
+
+ const rose = chrome ? boowomp : logo;
+
+ const currentMessage = writable<HeaderMessage|null>(null, (set) => {
+ let $currentMessage: HeaderMessage|null = null;
+ let currentTimeout: number|null = null;
+
+ const dismiss = () => {
+ if (currentTimeout == null) clearTimeout(currentTimeout);
+ $currentMessage = null;
+ set(null);
+ };
+
+ const unsubscribe = newestMessage.subscribe(($newestMessage) => {
+ if ($newestMessage == null) return;
+
+ if ($currentMessage != null) dismiss();
+
+ $currentMessage = $newestMessage;
+ $currentMessage.dismiss = dismiss;
+ if ($currentMessage.options.dismissable)
+ currentTimeout = setTimeout(dismiss, $currentMessage.options.duration);
+ set($currentMessage);
+ });
+
+ return () => {
+ unsubscribe();
+ dismiss();
+ };
+ });
+
+ setContext<Header>("Header", {
+ addMessage(...args: any[]) {
+
+ let [ message, options ] = args as [string, Partial<MessageOptions>|undefined];
+ options ??= { dismissable: true, duration: 5000 };
+ options.dismissable ??= true;
+ options.duration ??= 5000;
+ newestMessage.set({
+ type: "message",
+ message,
+ options: options as MessageOptions,
+ dismiss: void {},
+ });
+
+ },
+ });
+
+</script>
+
+<div class="main">
+ <header>
+ <div class="corner">
+ <a href="/" class="name">
+ <img id="logo" src={rose} alt="rose"/>
+ <p>rose</p>
+ </a>
+ </div>
+
+ {#if $currentMessage != null}
+ <div class="center" transition:fly>
+ <p>
+ {$currentMessage.message}
+ </p>
+ {#if $currentMessage.options.dismissable}
+ <button
+ class="dismiss"
+ on:click={$currentMessage.dismiss}>
+ <span class="icon">{@html close}</span>
+ </button>
+ {/if}
+ </div>
+ {/if}
+
+ <nav>
+ <ul>
+ <li aria-current={$page.url.pathname === '/' ? 'page' : undefined}>
+ <a href="/">Home</a>
+ </li>
+ <li aria-current={$page.url.pathname === '/projects' ? 'page' : undefined}>
+ <a href="/projects">Projects</a>
+ </li>
+ </ul>
+ </nav>
+ </header>
+ <slot />
+</div>
+
+<style lang="scss">
+ @use "sass:color";
+
+ @import "@fontsource-variable/playwrite-hr";
+
+ header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ width: 100%;
+ position: fixed;
+ position: -webkit-sticky;
+ top: 0;
+ z-index: 99;
+ background-color: color.change(white, $alpha: 0.9);
+ height: 3em;
+ }
+
+ .name {
+ font-family: 'Playwrite HR Variable', cursive;
+ font-size: 1.3em;
+ color: #150129;
+ padding-bottom: 6px;
+ }
+
+ .corner {
+ height: 3em;
+ margin-bottom: 0px;
+
+ & a {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+ text-decoration: none;
+
+ &:visited {
+ color: black;
+ }
+ &:hover &:active {
+ color: black;
+ }
+
+ & p {
+ padding-left: 4px;
+ }
+ }
+
+ & img {
+ width: 1.5em;
+ height: 1.5em;
+ object-fit: contain;
+ }
+
+ & img {
+ width: 1.5em;
+ height: 1.5em;
+ object-fit: contain;
+
+ }
+
+ }
+
+ .center {
+ display: flex;
+ flex-direction: row;
+ background-color: #000000ac;
+ border-radius: 100px;
+ align-items: center;
+ height: 2.5em;
+
+ & > .dismiss {
+ min-height: 2.3em;
+ min-width: 2.3em;
+ background: transparent;
+ border: none;
+ border-radius: 9999px;
+ margin-right: 10px;
+
+ &:hover { background: #ffffff30; }
+ &:active { background: #ffffff60; }
+
+ & > :global(span) {
+ justify-content: center;
+ align-content: center;
+ width: 100%;
+ height: 100%;
+ fill: #ffffff;
+ transform: translate(0, 0.125em);
+ & > :global(svg) {
+ width: 16px;
+ height: 16px;
+ object-fit: contain;
+ text-align: center;
+ fill: #ffffff;
+ }
+ }
+ }
+
+ & p {
+ color: #fff;
+ margin-left: 10px;
+ margin-right: 10px;
+ }
+
+ }
+
+ nav {
+ display: flex;
+ justify-content: right;
+ font-size: 1.2em;
+ padding-right: 10px;
+ object-fit: contain;
+ }
+
+ ul {
+ position: relative;
+ padding: 0;
+ margin: 0;
+ height: 3em;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ list-style: none;
+ background-size: contain;
+ }
+
+ li {
+ position: relative;
+ height: 100%;
+ }
+
+ li[aria-current='page']::before {
+ --size: 6px;
+ content: '';
+ width: 0;
+ height: 0;
+ position: absolute;
+ top: 0;
+ left: calc(50% - var(--size));
+ border: var(--size) solid transparent;
+ border-top: var(--size) solid var(--color-theme-1);
+ }
+
+ nav a {
+ display: flex;
+ height: 100%;
+ align-items: center;
+ padding: 0 0.5rem;
+ color: #150129;
+ font-weight: 700;
+ font-size-adjust: 0.3;
+ text-transform: uppercase;
+ letter-spacing: 0.1em;
+ text-decoration: none;
+ transition: color 0.2s linear;
+ }
+
+ a:hover {
+ color: var(--color-theme-1);
+ }
+
+ @media (prefers-color-scheme: dark) {
+ /* If the operating system is using dark mode, then apply this CSS */
+ header {
+ background-color: color.change(#292b33, $alpha: 0.9);
+ }
+
+ a:link:not(.permalink),a:visited:not(.permalink) {
+ color: #b9cffb;
+ }
+
+ .name {
+ color: #f5f5f5;
+ }
+ }
+
+</style>