Started work on a new version of the site
6
.gitignore
vendored
|
@ -1,14 +1,11 @@
|
|||
# build output
|
||||
dist/
|
||||
|
||||
# generated types
|
||||
.astro/
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
.netlify/
|
||||
|
||||
# logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
|
@ -21,3 +18,6 @@ pnpm-debug.log*
|
|||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
# jetbrains setting folder
|
||||
.idea/
|
2
.vscode/extensions.json
vendored
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"recommendations": ["astro-build.astro-vscode"],
|
||||
"unwantedRecommendations": []
|
||||
}
|
||||
}
|
2
.vscode/launch.json
vendored
|
@ -8,4 +8,4 @@
|
|||
"type": "node-terminal"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,31 +1,5 @@
|
|||
import { defineConfig } from "astro/config";
|
||||
import sitemap from "@astrojs/sitemap";
|
||||
import robotsTxt from "astro-robots-txt";
|
||||
import UnoCSS from "@unocss/astro";
|
||||
import icon from "astro-icon";
|
||||
|
||||
import solidJs from "@astrojs/solid-js";
|
||||
import { remarkReadingTime } from "./src/lib/ remark-reading-time.mjs";
|
||||
|
||||
import svelte from "@astrojs/svelte";
|
||||
// @ts-check
|
||||
import { defineConfig } from 'astro/config';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site: "https://toastiet0ast.com/",
|
||||
integrations: [
|
||||
sitemap(),
|
||||
robotsTxt({
|
||||
sitemap: [
|
||||
"https://toastiet0ast.com/sitemap-index.xml",
|
||||
"https://toastiet0ast.com/sitemap-0.xml",
|
||||
],
|
||||
}),
|
||||
solidJs(),
|
||||
UnoCSS({ injectReset: true }),
|
||||
icon(),
|
||||
svelte(),
|
||||
],
|
||||
markdown: {
|
||||
remarkPlugins: [remarkReadingTime],
|
||||
}
|
||||
});
|
||||
export default defineConfig({});
|
38
package.json
|
@ -1,45 +1,15 @@
|
|||
{
|
||||
"name": "astro-bento-portfolio",
|
||||
"name": "toastie-site",
|
||||
"type": "module",
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/rss": "^4.0.9",
|
||||
"@astrojs/sitemap": "^3.2.1",
|
||||
"@astrojs/solid-js": "^4.4.2",
|
||||
"@astrojs/svelte": "^5.7.2",
|
||||
"@iconify-json/ri": "^1.2.1",
|
||||
"@rive-app/canvas": "^2.21.6",
|
||||
"astro": "^4.16.5",
|
||||
"astro-icon": "^1.1.1",
|
||||
"astro-robots-txt": "^1.0.0",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"d3": "^7.9.0",
|
||||
"gsap": "^3.12.5",
|
||||
"lenis": "^1.1.14",
|
||||
"mdast-util-to-string": "^4.0.0",
|
||||
"motion": "^10.18.0",
|
||||
"reading-time": "^1.5.0",
|
||||
"solid-js": "^1.9.2",
|
||||
"svelte": "^4.2.19"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/d3": "^7.4.3",
|
||||
"@unocss/astro": "^0.63.4",
|
||||
"@unocss/postcss": "^0.63.4",
|
||||
"@unocss/preset-uno": "^0.63.4",
|
||||
"@unocss/reset": "^0.63.4",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"markdown-it": "^14.1.0",
|
||||
"motion": "^10.18.0",
|
||||
"sanitize-html": "^2.13.1",
|
||||
"typescript": "^5.6.3",
|
||||
"unocss": "^0.63.4"
|
||||
"astro": "^5.0.5"
|
||||
}
|
||||
}
|
||||
|
|
3425
pnpm-lock.yaml
|
@ -1,4 +0,0 @@
|
|||
// postcss.config.cjs
|
||||
module.exports = {
|
||||
plugins: {},
|
||||
};
|
BIN
public/assets/backgrounds/bg-main-dark-800w.jpg
Normal file
After Width: | Height: | Size: 12 KiB |
1
public/assets/backgrounds/bg-main-dark.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="640" width="1440" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a"><stop offset=".58" stop-opacity="0"/><stop offset="1"/></linearGradient><linearGradient id="b" gradientUnits="userSpaceOnUse" x1="793.5" x2="759.5" xlink:href="#a" y1="261.5" y2="149.5"/><linearGradient id="c" gradientUnits="userSpaceOnUse" x1="644.19" x2="645.54" xlink:href="#a" y1="398.02" y2="267.7"/><linearGradient id="d" gradientUnits="userSpaceOnUse" x1="547" x2="522.36" xlink:href="#a" y1="457.27" y2="342.85"/><g clip-rule="evenodd" fill-rule="evenodd" opacity=".15"><path d="m439.57 249.55a2149.47 2149.47 0 0 1 1193.87-182.45l-12.48 93.17a2055.46 2055.46 0 0 0 -1141.66 174.47l-454.24 211.86-39.73-85.2z" fill="url(#b)"/><path d="m272.3 266.93a2393.36 2393.36 0 0 1 1328.96 205.6l-44.42 94.78a2288.7 2288.7 0 0 0 -1270.84-196.61l-553.29 73.05-13.7-103.77z" fill="url(#c)" opacity=".56"/><path d="m195.26 416.13a2149.46 2149.46 0 0 1 1204.86-83.21l-20.13 91.82a2055.46 2055.46 0 0 0 -1152.17 79.56l-470.18 173.62-32.56-88.18 470.18-173.62z" fill="url(#d)"/></g><path d="m-258.15 719.56 1743.12-517.56 182.93 616.12-1743.1 517.56z" fill="#090b11"/></svg>
|
After Width: | Height: | Size: 1.2 KiB |
BIN
public/assets/backgrounds/bg-main-light-800w.jpg
Normal file
After Width: | Height: | Size: 14 KiB |
1
public/assets/backgrounds/bg-main-light.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1440" height="640"><g opacity=".15"><path fill="url(#a)" d="M439.57 249.55A2149.47 2149.47 0 0 1 1633.44 67.1l-12.48 93.17A2055.46 2055.46 0 0 0 479.3 334.74L25.06 546.6l-39.73-85.2z"/><path fill="url(#b)" d="M272.3 265.93a2393.36 2393.36 0 0 1 1328.96 205.6l-44.42 94.78A2288.7 2288.7 0 0 0 286 369.7l-553.29 73.05-13.7-103.77z" opacity=".56"/><path fill="url(#c)" d="M195.26 416.13a2149.47 2149.47 0 0 1 1204.86-83.21l-20.13 91.82A2055.46 2055.46 0 0 0 227.82 504.3l-470.18 173.62-32.56-88.18 470.18-173.62z"/></g><path fill="#fff" d="M-258 718.56 1485.12 201l182.93 616.12-1743.11 517.56z"/><defs><linearGradient id="d"><stop offset=".58" stop-opacity="0"/><stop offset="1"/></linearGradient><linearGradient xlink:href="#d" id="a" x1="793.5" x2="759.5" y1="261.5" y2="149.5" gradientUnits="userSpaceOnUse"/><linearGradient xlink:href="#d" id="b" x1="644.19" x2="645.54" y1="397.02" y2="266.7" gradientUnits="userSpaceOnUse"/><linearGradient xlink:href="#d" id="c" x1="547" x2="522.36" y1="457.27" y2="342.85" gradientUnits="userSpaceOnUse"/></defs></svg>
|
After Width: | Height: | Size: 1.1 KiB |
BIN
public/assets/backgrounds/noise.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
public/assets/dcs.png
Normal file
After Width: | Height: | Size: 114 KiB |
BIN
public/assets/ellie-casey.png
Normal file
After Width: | Height: | Size: 273 KiB |
BIN
public/assets/toastielab.png
Normal file
After Width: | Height: | Size: 346 KiB |
BIN
public/assets/valkyriecoms.png
Normal file
After Width: | Height: | Size: 89 KiB |
|
@ -1,13 +0,0 @@
|
|||
---
|
||||
import Card from "./Card/index.astro";
|
||||
---
|
||||
|
||||
<Card colSpan="md:col-span-1" rowSpan="md:row-span-6" title="About me">
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="text-sm font-light">
|
||||
<p>
|
||||
Hi, I'm Toastie_t0ast, a software dev & systems administrator from New Zealand.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
|
@ -1,29 +0,0 @@
|
|||
---
|
||||
import { formatDate } from "../../lib/helpers";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
date: Date;
|
||||
url: string;
|
||||
}
|
||||
|
||||
const { title, date, url } = Astro.props;
|
||||
---
|
||||
|
||||
<li class="w-full text-neutral-100 hover:text-neutral-400 ease-in-out transition-colors border-b-neutral-400 border-dashed border-b-1 my-2">
|
||||
<a
|
||||
class="text-sm md:text-base decoration-none flex justify-between"
|
||||
href={`/blog/${url}`}
|
||||
>
|
||||
<p class="inline-block whitespace-nowrap">
|
||||
{title}
|
||||
</p>
|
||||
<time
|
||||
class="text-right tabular-nums"
|
||||
datetime={date.toISOString()}
|
||||
data-date={date.toISOString()}
|
||||
>
|
||||
{formatDate(date)}
|
||||
</time>
|
||||
</a>
|
||||
</li>
|
|
@ -1,15 +0,0 @@
|
|||
---
|
||||
interface Props {
|
||||
rounded?: boolean;
|
||||
}
|
||||
|
||||
const { rounded } = Astro.props;
|
||||
---
|
||||
|
||||
<button
|
||||
class={`custom-btn text-xl max-h-[50px] shadow-custom shadow-primary-500 active:shadow-none active:translate-x-[3px] active:translate-y-[3px] text-gray-200 px-5 py-2 border border-primary-500 hover:text-primary-500 transition-colors duration-100 ease-in-out bg-gray-900 cursor-pointer ${
|
||||
rounded ? "rounded-full" : "rounded-lg"
|
||||
}`}
|
||||
>
|
||||
<slot />
|
||||
</button>
|
56
src/components/CallToAction.astro
Normal file
|
@ -0,0 +1,56 @@
|
|||
---
|
||||
interface Props {
|
||||
href: string;
|
||||
}
|
||||
|
||||
const { href } = Astro.props;
|
||||
---
|
||||
|
||||
<a href={href}><slot /></a>
|
||||
|
||||
<style>
|
||||
a {
|
||||
position: relative;
|
||||
display: flex;
|
||||
place-content: center;
|
||||
text-align: center;
|
||||
padding: 0.56em 2em;
|
||||
gap: 0.8em;
|
||||
color: var(--accent-text-over);
|
||||
text-decoration: none;
|
||||
line-height: 1.1;
|
||||
border-radius: 999rem;
|
||||
overflow: hidden;
|
||||
background: var(--gradient-accent-orange);
|
||||
box-shadow: var(--shadow-md);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media (min-width: 20em) {
|
||||
a {
|
||||
font-size: var(--text-lg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Overlay for hover effects. */
|
||||
a::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
transition: background-color var(--theme-transition);
|
||||
mix-blend-mode: overlay;
|
||||
}
|
||||
|
||||
a:focus::after,
|
||||
a:hover::after {
|
||||
background-color: hsla(var(--gray-999-basis), 0.3);
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
a {
|
||||
padding: 1.125rem 2.5rem;
|
||||
font-size: var(--text-xl);
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,14 +0,0 @@
|
|||
---
|
||||
interface Props {
|
||||
title?: string;
|
||||
body?: string;
|
||||
}
|
||||
|
||||
const { title, body } = Astro.props;
|
||||
---
|
||||
|
||||
<>
|
||||
{title && <h2 class="text-xl font-bold m-0 z-20">{title}</h2>}
|
||||
{body && <p class="m-0 font-light text-base">{body}</p>}
|
||||
<slot />
|
||||
</>
|
|
@ -1,43 +0,0 @@
|
|||
---
|
||||
import { Icon } from "astro-icon/components";
|
||||
import Content from "./Content.astro";
|
||||
|
||||
interface Props {
|
||||
title?: string;
|
||||
body?: string;
|
||||
colSpan?: string;
|
||||
rowSpan?: string;
|
||||
href?: string;
|
||||
colorText?: string;
|
||||
height?: string;
|
||||
width?: string;
|
||||
}
|
||||
|
||||
const { title, body, colSpan, rowSpan, href, colorText, height } = Astro.props;
|
||||
---
|
||||
|
||||
<div
|
||||
class={`card h-max sm:h-auto group overflow-hidden transform-y-[-40%] bg-darkslate-500 shadow-lg rounded-lg p-6 border border-darkslate-100 hover:border-primary-500 align-start flex-none ${
|
||||
height || "h-full"
|
||||
} justify-start relative transform perspective-1200 w-full transition duration-75 ease-in-out col-span-1 ${
|
||||
colSpan || "md:col-span-2"
|
||||
} ${rowSpan || ""}`}
|
||||
>
|
||||
{
|
||||
href ? (
|
||||
<a href={href} class={`h-full w-full ${colorText || " "}`}>
|
||||
<Icon
|
||||
name="ri:arrow-right-up-line"
|
||||
class="h-6 float-right group-hover:text-primary-500 group-hover:translate-x-1 group-hover:-translate-y-1 transition-transform ease-in-out duration-100 z-20"
|
||||
/>
|
||||
<Content title={title} body={body}>
|
||||
<slot />
|
||||
</Content>
|
||||
</a>
|
||||
) : (
|
||||
<Content title={title} body={body}>
|
||||
<slot />
|
||||
</Content>
|
||||
)
|
||||
}
|
||||
</div>
|
46
src/components/ContactCTA.astro
Normal file
|
@ -0,0 +1,46 @@
|
|||
---
|
||||
import CallToAction from './CallToAction.astro';
|
||||
import Icon from './Icon.astro';
|
||||
---
|
||||
|
||||
<aside>
|
||||
<h2>Need to contact me?</h2>
|
||||
<CallToAction href="mailto:hello@toastiet0ast.com">
|
||||
Send Me an Email
|
||||
<Icon icon="paper-plane-tilt" size="1.2em" />
|
||||
</CallToAction>
|
||||
</aside>
|
||||
|
||||
<style>
|
||||
aside {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 3rem;
|
||||
border-top: 1px solid var(--gray-800);
|
||||
border-bottom: 1px solid var(--gray-800);
|
||||
padding: 5rem 1.5rem;
|
||||
background-color: var(--gray-999_40);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: var(--text-xl);
|
||||
text-align: center;
|
||||
max-width: 15ch;
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
aside {
|
||||
padding: 7.5rem;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: var(--text-3xl);
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,32 +0,0 @@
|
|||
---
|
||||
import Card from "./Card/index.astro";
|
||||
import { LINKS } from "../lib/constants";
|
||||
---
|
||||
|
||||
<Card colSpan="md:col-span-1" rowSpan="md:row-span-4">
|
||||
<div class="h-full">
|
||||
<header class="flex items-center">
|
||||
<h1 class="text-white text-xl font-bold">
|
||||
Let's start working together!
|
||||
</h1>
|
||||
</header>
|
||||
<address class="flex flex-col mt-4">
|
||||
<h2 class="text-gray-500">Contact Details</h2>
|
||||
<p>hello@toastiet0ast.com</p>
|
||||
</address>
|
||||
<div class="flex flex-col mt-4 w-fit">
|
||||
<h2 class="text-gray-500">Socials</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href={LINKS.toastielab} target="_blank">Toastielab</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href={LINKS.discord} target="_blank">Discord</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href={LINKS.valkyriecoms} target="_blank">Valkyriecoms</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
70
src/components/Footer.astro
Normal file
|
@ -0,0 +1,70 @@
|
|||
---
|
||||
import Icon from './Icon.astro';
|
||||
const currentYear = new Date().getFullYear();
|
||||
---
|
||||
|
||||
<footer>
|
||||
<div class="group">
|
||||
<p>© {currentYear} Toastie_t0ast</p>
|
||||
</div>
|
||||
<p class="socials">
|
||||
<a href="https://valkyriecoms.com/@toastie"> Valkyriecoms</a>
|
||||
<a href="https://toastielab.dev/toastie_t0ast"> Toastielab</a>
|
||||
<a href="https://discord.gg/3qvVNTk6sa"> Discord</a>
|
||||
</p>
|
||||
</footer>
|
||||
<style>
|
||||
footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3rem;
|
||||
margin-top: auto;
|
||||
padding: 3rem 2rem 3rem;
|
||||
text-align: center;
|
||||
color: var(--gray-400);
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
|
||||
footer a {
|
||||
color: var(--gray-400);
|
||||
text-decoration: 1px solid underline transparent;
|
||||
text-underline-offset: 0.25em;
|
||||
transition: text-decoration-color var(--theme-transition);
|
||||
}
|
||||
|
||||
footer a:hover,
|
||||
footer a:focus {
|
||||
text-decoration-color: currentColor;
|
||||
}
|
||||
|
||||
.group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.socials {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
footer {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
padding: 2.5rem 5rem;
|
||||
}
|
||||
|
||||
.group {
|
||||
flex-direction: row;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.socials {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
</style>
|
65
src/components/Grid.astro
Normal file
|
@ -0,0 +1,65 @@
|
|||
---
|
||||
interface Props {
|
||||
variant?: 'offset' | 'small'
|
||||
}
|
||||
|
||||
const { variant } = Astro.props;
|
||||
---
|
||||
|
||||
<ul class:list={['grid', { offset: variant === 'offset', small: variant === 'small' }]}>
|
||||
<slot />
|
||||
</ul>
|
||||
|
||||
<style>
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-auto-rows: 1fr;
|
||||
gap: 1rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.grid.small {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
/* If last row contains only one item, make it span both columns. */
|
||||
.grid.small > :global(:last-child:nth-child(odd)) {
|
||||
grid-column: 1 / 3;
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
.grid {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 4rem;
|
||||
}
|
||||
|
||||
.grid.offset {
|
||||
--row-offset: 7.5rem;
|
||||
padding-bottom: var(--row-offset);
|
||||
}
|
||||
|
||||
/* Shift first item in each row vertically to create staggered effect. */
|
||||
.grid.offset > :global(:nth-child(odd)) {
|
||||
transform: translateY(var(--row-offset));
|
||||
}
|
||||
|
||||
/* If last row contains only one item, display it in the second column. */
|
||||
.grid.offset > :global(:last-child:nth-child(odd)) {
|
||||
grid-column: 2 / 3;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.grid.small {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.grid.small > :global(*) {
|
||||
flex-basis: 20rem;
|
||||
}
|
||||
}
|
||||
</style>
|
54
src/components/Hero.astro
Normal file
|
@ -0,0 +1,54 @@
|
|||
---
|
||||
interface Props {
|
||||
title: string;
|
||||
tagline?: string;
|
||||
align?: 'start' | 'center';
|
||||
}
|
||||
|
||||
const { align = 'center', tagline, title } = Astro.props;
|
||||
---
|
||||
|
||||
<div class:list={['hero stack gap-4', align]}>
|
||||
<div class="stack gap-2">
|
||||
<h1 class="title">{title}</h1>
|
||||
{tagline && <p class="tagline">{tagline}</p>}
|
||||
</div>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.hero {
|
||||
font-size: var(--text-lg);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.title,
|
||||
.tagline {
|
||||
max-width: 37ch;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: var(--text-3xl);
|
||||
color: var(--gray-0);
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
.hero {
|
||||
font-size: var(--text-xl);
|
||||
}
|
||||
|
||||
.start {
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
.start .title,
|
||||
.start .tagline {
|
||||
margin-inline: unset;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: var(--text-5xl);
|
||||
}
|
||||
}
|
||||
</style>
|
56
src/components/Icon.astro
Normal file
|
@ -0,0 +1,56 @@
|
|||
---
|
||||
import type { HTMLAttributes } from 'astro/types';
|
||||
import { iconPaths } from './IconPaths';
|
||||
|
||||
interface Props {
|
||||
icon: keyof typeof iconPaths;
|
||||
color?: string;
|
||||
gradient?: boolean;
|
||||
size?: string;
|
||||
}
|
||||
|
||||
const { color = 'currentcolor', gradient, icon, size } = Astro.props;
|
||||
const iconPath = iconPaths[icon];
|
||||
|
||||
const attrs: HTMLAttributes<'svg'> = {};
|
||||
if (size) attrs.style = { '--size': size };
|
||||
|
||||
const gradientId = 'icon-gradient-' + Math.round(Math.random() * 10e12).toString(36);
|
||||
---
|
||||
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 256 256"
|
||||
aria-hidden="true"
|
||||
stroke={gradient ? `url(#${gradientId})` : color}
|
||||
fill={gradient ? `url(#${gradientId})` : color}
|
||||
{...attrs}
|
||||
>
|
||||
<g set:html={iconPath} />
|
||||
{
|
||||
gradient && (
|
||||
<linearGradient
|
||||
id={gradientId}
|
||||
x1="23"
|
||||
x2="235"
|
||||
y1="43"
|
||||
y2="202"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="var(--gradient-stop-1)" />
|
||||
<stop offset=".5" stop-color="var(--gradient-stop-2)" />
|
||||
<stop offset="1" stop-color="var(--gradient-stop-3)" />
|
||||
</linearGradient>
|
||||
)
|
||||
}
|
||||
</svg>
|
||||
|
||||
<style>
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
width: var(--size, 1em);
|
||||
height: var(--size, 1em);
|
||||
}
|
||||
</style>
|
39
src/components/IconPaths.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* Icons adapted from https://phosphoricons.com/
|
||||
*
|
||||
* Want to add more?
|
||||
* 1. Find the icon you want on Phosphor Icons.
|
||||
* 2. Click “Copy SVG”.
|
||||
* 3. Paste the SVG code in your editor.
|
||||
* 4. Remove the `<svg>` wrapper so you only have elements like `<path>`, `<circle>`, `<rect>` etc.
|
||||
* 5. Remove any `stroke="#000000"` attributes
|
||||
* 6. Replace any `fill="#000000"` attributes with `stroke="none"`
|
||||
* (or add `stroke="none"` on shapes with no `fill` or `stroke` specified).
|
||||
*/
|
||||
export const iconPaths = {
|
||||
'terminal-window': `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="m80 96 40 32-40 32m56 0h40"/><rect width="192" height="160" x="32" y="48" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16.97" rx="8.5"/>`,
|
||||
trophy: `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M56 56v55.1c0 39.7 31.8 72.6 71.5 72.9a72 72 0 0 0 72.5-72V56a8 8 0 0 0-8-8H64a8 8 0 0 0-8 8Zm40 168h64m-32-40v40"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M198.2 128h9.8a32 32 0 0 0 32-32V80a8 8 0 0 0-8-8h-32M58 128H47.9a32 32 0 0 1-32-32V80a8 8 0 0 1 8-8h32"/>`,
|
||||
strategy: `<circle cx="68" cy="188" r="28" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="m40 72 40 40m0-40-40 40m136 56 40 40m0-40-40 40M136 80V40h40"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="m136 40 16 16c40 40 8 88-24 96"/>`,
|
||||
'paper-plane-tilt': `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M210.3 35.9 23.9 88.4a8 8 0 0 0-1.2 15l85.6 40.5a7.8 7.8 0 0 1 3.8 3.8l40.5 85.6a8 8 0 0 0 15-1.2l52.5-186.4a7.9 7.9 0 0 0-9.8-9.8Zm-99.4 109.2 45.2-45.2"/>`,
|
||||
'arrow-right': `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M40 128h176m-72-72 72 72-72 72"/>`,
|
||||
'arrow-left': `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M216 128H40m72-72-72 72 72 72"/>`,
|
||||
code: `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="m64 88-48 40 48 40m128-80 48 40-48 40M160 40 96 216"/>`,
|
||||
'microphone-stage': `<circle cx="168" cy="88" r="64" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="m213.3 133.3-90.6-90.6M100 156l-12 12m16.8-70.1L28.1 202.5a7.9 7.9 0 0 0 .8 10.4l14.2 14.2a7.9 7.9 0 0 0 10.4.8l104.6-76.7"/>`,
|
||||
'pencil-line': `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M96 216H48a8 8 0 0 1-8-8v-44.7a7.9 7.9 0 0 1 2.3-5.6l120-120a8 8 0 0 1 11.4 0l44.6 44.6a8 8 0 0 1 0 11.4Zm40-152 56 56"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M216 216H96l-55.5-55.5M164 92l-96 96"/>`,
|
||||
'rocket-launch': `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M94.1 184.6c-11.4 33.9-56.6 33.9-56.6 33.9s0-45.2 33.9-56.6m124.5-56.5L128 173.3 82.7 128l67.9-67.9C176.3 34.4 202 34.7 213 36.3a7.8 7.8 0 0 1 6.7 6.7c1.6 11 1.9 36.7-23.8 62.4Z"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M184.6 116.7v64.6a8 8 0 0 1-2.4 5.6l-32.3 32.4a8 8 0 0 1-13.5-4.1l-8.4-41.9m11.3-101.9H74.7a8 8 0 0 0-5.6 2.4l-32.4 32.3a8 8 0 0 0 4.1 13.5l41.9 8.4"/>`,
|
||||
list: `<path stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M40 128h176M40 64h176M40 192h176"/>`,
|
||||
heart: `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M128 216S28 160 28 92a52 52 0 0 1 100-20h0a52 52 0 0 1 100 20c0 68-100 124-100 124Z"/>`,
|
||||
'moon-stars': `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M216 112V64m24 24h-48m-24-64v32m16-16h-32m65 113A92 92 0 0 1 103 39h0a92 92 0 1 0 114 114Z"/>`,
|
||||
sun: `<circle cx="128" cy="128" r="60" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M128 36V16M63 63 49 49m-13 79H16m47 65-14 14m79 13v20m65-47 14 14m13-79h20m-47-65 14-14"/>`,
|
||||
'twitter-logo': `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M128 88c0-22 18.5-40.3 40.5-40a40 40 0 0 1 36.2 24H240l-32.3 32.3A127.9 127.9 0 0 1 80 224c-32 0-40-12-40-12s32-12 48-36c0 0-64-32-48-120 0 0 40 40 88 48Z"/>`,
|
||||
'codepen-logo': `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="m232 101-104 59-104-59 100.1-56.8a8.3 8.3 0 0 1 7.8 0Z"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="m232 165-100.1 56.8a8.3 8.3 0 0 1-7.8 0L24 165l104-59Zm0-64v64M24 101v64m104-5v62.8m0-179.6V106"/>`,
|
||||
'git-branch': `<path d="M232,64a32,32,0,1,0-40,31v17a8,8,0,0,1-8,8H96a23.84,23.84,0,0,0-8,1.38V95a32,32,0,1,0-16,0v66a32,32,0,1,0,16,0V144a8,8,0,0,1,8-8h88a24,24,0,0,0,24-24V95A32.06,32.06,0,0,0,232,64ZM64,64A16,16,0,1,1,80,80,16,16,0,0,1,64,64ZM96,192a16,16,0,1,1-16-16A16,16,0,0,1,96,192ZM200,80a16,16,0,1,1,16-16A16,16,0,0,1,200,80Z"></path>`,
|
||||
'twitch-logo': `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M165 200h-42a8 8 0 0 0-5 2l-46 38v-40H48a8 8 0 0 1-8-8V48a8 8 0 0 1 8-8h160a8 8 0 0 1 8 8v108a8 8 0 0 1-3 6l-43 36a8 8 0 0 1-5 2Zm3-112v48m-48-48v48"/>`,
|
||||
'youtube-logo': `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="m160 128-48-32v64l48-32z"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M24 128c0 30 3 47 5 56a16 16 0 0 0 10 11c34 13 89 13 89 13s56 0 89-13a16 16 0 0 0 10-11c2-9 5-26 5-56s-3-47-5-56a16 16 0 0 0-10-11c-33-13-89-13-89-13s-55 0-89 13a16 16 0 0 0-10 11c-2 9-5 26-5 56Z"/>`,
|
||||
'dribbble-logo': `<circle cx="128" cy="128" r="96" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M71 205a160 160 0 0 1 137-77l16 1m-36-76a160 160 0 0 1-124 59 165 165 0 0 1-30-3"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M86 42a161 161 0 0 1 74 177"/>`,
|
||||
'discord-logo': `<circle stroke="none" cx="96" cy="144" r="12"/><circle stroke="none" cx="160" cy="144" r="12"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M74 80a175 175 0 0 1 54-8 175 175 0 0 1 54 8m0 96a175 175 0 0 1-54 8 175 175 0 0 1-54-8"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="m155 182 12 24a8 8 0 0 0 9 4c25-6 46-16 61-30a8 8 0 0 0 3-8L206 59a8 8 0 0 0-5-5 176 176 0 0 0-30-9 8 8 0 0 0-9 5l-8 24m-53 108-12 24a8 8 0 0 1-9 4c-25-6-46-16-61-30a8 8 0 0 1-3-8L50 59a8 8 0 0 1 5-5 176 176 0 0 1 30-9 8 8 0 0 1 9 5l8 24"/>`,
|
||||
'linkedin-logo': `<rect width="184" height="184" x="36" y="36" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" rx="8"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M120 112v64m-32-64v64m32-36a28 28 0 0 1 56 0v36"/><circle stroke="none" cx="88" cy="80" r="12"/>`,
|
||||
'instagram-logo': `<circle cx="128" cy="128" r="40" fill="none" stroke-miterlimit="10" stroke-width="16"/><rect width="184" height="184" x="36" y="36" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" rx="48"/><circle cx="180" cy="76" r="12" stroke="none" />`,
|
||||
'tiktok-logo': `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M168 106a96 96 0 0 0 56 18V84a56 56 0 0 1-56-56h-40v128a28 28 0 1 1-40-25V89a68 68 0 1 0 80 67Z"/>`,
|
||||
'fediverse-logo': '<path d="M212,96a27.84,27.84,0,0,0-10.51,2L171,59.94A28,28,0,1,0,120,44a28.65,28.65,0,0,0,.15,2.94L73.68,66.3a28,28,0,1,0-28.6,44.83l1.85,46.38a28,28,0,1,0,32.74,41.42L128,212.47a28,28,0,1,0,49.13-18.79l27.21-42.75A28,28,0,1,0,212,96Zm-56,88-.89,0-16.18-48.53,46.65-2.22a27.94,27.94,0,0,0,5.28,9l-27.21,42.75A28,28,0,0,0,156,184ZM62.92,156.87l-1.85-46.38a28,28,0,0,0,10.12-6.13L113.72,129,72.26,161.22A28,28,0,0,0,62.92,156.87ZM149.57,72a27.8,27.8,0,0,0,8.94-2L189,108.06a27.86,27.86,0,0,0-4.18,9.22l-46.57,2.22ZM82.09,173.85,124,141.26l15.94,47.83a28.2,28.2,0,0,0-7.6,8L84,183.53A28,28,0,0,0,82.09,173.85ZM148,32a12,12,0,1,1-12,12A12,12,0,0,1,148,32ZM126.32,61.7A28.44,28.44,0,0,0,134,68.24l-11.3,47.45L79.23,90.52A28,28,0,0,0,80,84a28.65,28.65,0,0,0-.15-2.94ZM40,84A12,12,0,1,1,52,96,12,12,0,0,1,40,84ZM56,196a12,12,0,1,1,12-12A12,12,0,0,1,56,196Zm100,28a12,12,0,1,1,12-12A12,12,0,0,1,156,224Zm56-88a12,12,0,1,1,12-12A12,12,0,0,1,212,136Z"></path>'
|
||||
};
|
|
@ -1,36 +0,0 @@
|
|||
---
|
||||
import Card from "./Card/index.astro";
|
||||
import Button from "./Button.astro";
|
||||
import { LINKS } from "../lib/constants";
|
||||
import { Icon } from 'astro-icon/components'
|
||||
import Tooltip from "./Tooltip/index";
|
||||
---
|
||||
|
||||
<Card colSpan="md:col-span-3" rowSpan="md:row-span-4">
|
||||
<div class="flex w-full h-full">
|
||||
<div class="flex flex-col justify-between md:max-h-[300px] gap-4">
|
||||
<div class="flex flex-col h-full">
|
||||
<h6 class="text-sm font-light m-0 text-gray-500">welcome</h6>
|
||||
<p class="m-0 font-light text-xl">
|
||||
Hi, I'm <b class="font-bold">Toastie_t0ast</b>, I like to make
|
||||
Discord Bots as well as work on game servers through my host
|
||||
Dragon's child hosting.
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex gap-4">
|
||||
<a href={LINKS.toastielab} aria-label="toastielab profile" target="_blank">
|
||||
<Button aria-label="toastielab profile">
|
||||
<Icon name="ri:github-fill" class="h-6" />
|
||||
<span class="sr-only">Toastielab Profile</span>
|
||||
</Button>
|
||||
</a>
|
||||
<Tooltip client:visible>
|
||||
<Button aria-label="easter egg btn">
|
||||
<Icon name="ri:emotion-laugh-line" class="h-6" />
|
||||
<span class="sr-only">Easter egg button</span>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
47
src/components/MainHead.astro
Normal file
|
@ -0,0 +1,47 @@
|
|||
---
|
||||
import '../styles/global.css';
|
||||
|
||||
interface Props {
|
||||
title?: string | undefined;
|
||||
description?: string | undefined;
|
||||
}
|
||||
|
||||
const {
|
||||
title = 'Toastie_t0ast',
|
||||
description = 'A random dev',
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="description" property="og:description" content={description} />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<title>{title}</title>
|
||||
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Public+Sans:ital,wght@0,400;0,700;1,400&family=Rubik:wght@500;600&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<script is:inline>
|
||||
// This code is inlined in the head to make dark mode instant & blocking.
|
||||
const getThemePreference = () => {
|
||||
if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) {
|
||||
return localStorage.getItem('theme');
|
||||
}
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
};
|
||||
const isDark = getThemePreference() === 'dark';
|
||||
document.documentElement.classList[isDark ? 'add' : 'remove']('theme-dark');
|
||||
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
// Watch the document element and persist user preference when it changes.
|
||||
const observer = new MutationObserver(() => {
|
||||
const isDark = document.documentElement.classList.contains('theme-dark');
|
||||
localStorage.setItem('theme', isDark ? 'dark' : 'light');
|
||||
});
|
||||
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
|
||||
}
|
||||
</script>
|
352
src/components/Nav.astro
Normal file
|
@ -0,0 +1,352 @@
|
|||
---
|
||||
import Icon from './Icon.astro';
|
||||
import ThemeToggle from './ThemeToggle.astro';
|
||||
import type { iconPaths } from './IconPaths';
|
||||
|
||||
/** Main menu items */
|
||||
const textLinks: { label: string; href: string }[] = [
|
||||
{ label: 'Home', href: '/' },
|
||||
{ label: 'Work', href: '/work/' },
|
||||
{ label: 'About', href: '/about/' },
|
||||
];
|
||||
|
||||
/** Icon links to social media — edit these with links to your profiles! */
|
||||
const iconLinks: { label: string; href: string; icon: keyof typeof iconPaths }[] = [
|
||||
{ label: 'Valkyriecoms', href: 'https://valkyriecoms.com/@toastie', icon: 'fediverse-logo' },
|
||||
{ label: 'Twitch', href: 'https://twitch.tv/toastie_t0ast', icon: 'twitch-logo' },
|
||||
{ label: 'Toastielab', href: 'https://toastielab.dev/toastie_t0ast', icon: 'git-branch' },
|
||||
{ label: 'Discord', href: 'https://discord.gg/3qvVNTk6sa', icon: 'discord-logo' },
|
||||
{ label: 'YouTube', href: 'https://www.youtube.com/@toastie_t0ast', icon: 'youtube-logo' },
|
||||
];
|
||||
|
||||
/** Test if a link is pointing to the current page. */
|
||||
const isCurrentPage = (href: string) => {
|
||||
let pathname = Astro.url.pathname.replace(import.meta.env.BASE_URL, '');
|
||||
if (pathname.at(0) !== '/') pathname = '/' + pathname;
|
||||
if (pathname.at(-1) !== '/') pathname += '/';
|
||||
return pathname === href || (href !== '/' && pathname.startsWith(href));
|
||||
};
|
||||
---
|
||||
|
||||
<nav>
|
||||
<div class="menu-header">
|
||||
<a href="/" class="site-title">
|
||||
<Icon icon="terminal-window" color="var(--accent-regular)" size="1.6em" gradient />
|
||||
Toastie_t0ast
|
||||
</a>
|
||||
<menu-button>
|
||||
<template>
|
||||
<button class="menu-button" aria-expanded="false">
|
||||
<span class="sr-only">Menu</span>
|
||||
<Icon icon="list" />
|
||||
</button>
|
||||
</template>
|
||||
</menu-button>
|
||||
</div>
|
||||
<noscript>
|
||||
<ul class="nav-items">
|
||||
{
|
||||
textLinks.map(({ label, href }) => (
|
||||
<li>
|
||||
<a aria-current={isCurrentPage(href) ? 'page' : null} class="link" href={href}>
|
||||
{label}
|
||||
</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</noscript>
|
||||
<noscript>
|
||||
<div class="menu-footer">
|
||||
<div class="socials">
|
||||
{
|
||||
iconLinks.map(({ href, icon, label }) => (
|
||||
<a href={href} class="social">
|
||||
<span class="sr-only">{label}</span>
|
||||
<Icon icon={icon} />
|
||||
</a>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</noscript>
|
||||
<div id="menu-content" hidden>
|
||||
<ul class="nav-items">
|
||||
{
|
||||
textLinks.map(({ label, href }) => (
|
||||
<li>
|
||||
<a aria-current={isCurrentPage(href) ? 'page' : null} class="link" href={href}>
|
||||
{label}
|
||||
</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
<div class="menu-footer">
|
||||
<div class="socials">
|
||||
{
|
||||
iconLinks.map(({ href, icon, label }) => (
|
||||
<a href={href} class="social">
|
||||
<span class="sr-only">{label}</span>
|
||||
<Icon icon={icon} />
|
||||
</a>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="theme-toggle">
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<script>
|
||||
class MenuButton extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
// Inject menu toggle button when JS runs.
|
||||
this.appendChild(this.querySelector('template')!.content.cloneNode(true));
|
||||
const btn = this.querySelector('button')!;
|
||||
|
||||
// Hide menu (shown by default to support no-JS browsers).
|
||||
const menu = document.getElementById('menu-content')!;
|
||||
menu.hidden = true;
|
||||
// Add "menu-content" class in JS to avoid covering content in non-JS browsers.
|
||||
menu.classList.add('menu-content');
|
||||
|
||||
/** Set whether the menu is currently expanded or collapsed. */
|
||||
const setExpanded = (expand: boolean) => {
|
||||
btn.setAttribute('aria-expanded', expand ? 'true' : 'false');
|
||||
menu.hidden = !expand;
|
||||
};
|
||||
|
||||
// Toggle menu visibility when the menu button is clicked.
|
||||
btn.addEventListener('click', () => setExpanded(menu.hidden));
|
||||
|
||||
// Hide menu button for large screens.
|
||||
const handleViewports = (e: MediaQueryList | MediaQueryListEvent) => {
|
||||
setExpanded(e.matches);
|
||||
btn.hidden = e.matches;
|
||||
};
|
||||
const mediaQueries = window.matchMedia('(min-width: 50em)');
|
||||
handleViewports(mediaQueries);
|
||||
mediaQueries.addEventListener('change', handleViewports);
|
||||
}
|
||||
}
|
||||
customElements.define('menu-button', MenuButton);
|
||||
</script>
|
||||
|
||||
<style>
|
||||
nav {
|
||||
z-index: 9999;
|
||||
position: relative;
|
||||
font-family: var(--font-brand);
|
||||
font-weight: 500;
|
||||
margin-bottom: 3.5rem;
|
||||
}
|
||||
|
||||
.menu-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 0.5rem;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.site-title {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
line-height: 1.1;
|
||||
color: var(--gray-0);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.menu-button {
|
||||
position: relative;
|
||||
display: flex;
|
||||
border: 0;
|
||||
border-radius: 999rem;
|
||||
padding: 0.5rem;
|
||||
font-size: 1.5rem;
|
||||
color: var(--gray-300);
|
||||
background: radial-gradient(var(--gray-900), var(--gray-800) 150%);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.menu-button[aria-expanded='true'] {
|
||||
color: var(--gray-0);
|
||||
background: linear-gradient(180deg, var(--gray-600), transparent),
|
||||
radial-gradient(var(--gray-900), var(--gray-800) 150%);
|
||||
}
|
||||
|
||||
.menu-button[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.menu-button::before {
|
||||
position: absolute;
|
||||
inset: -1px;
|
||||
content: '';
|
||||
background: var(--gradient-stroke);
|
||||
border-radius: 999rem;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.menu-content {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.nav-items {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
font-size: var(--text-md);
|
||||
line-height: 1.2;
|
||||
list-style: none;
|
||||
padding: 2rem;
|
||||
background-color: var(--gray-999);
|
||||
border-bottom: 1px solid var(--gray-800);
|
||||
}
|
||||
|
||||
.link {
|
||||
display: inline-block;
|
||||
color: var(--gray-300);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.link[aria-current] {
|
||||
color: var(--gray-0);
|
||||
}
|
||||
|
||||
.menu-footer {
|
||||
--icon-size: var(--text-xl);
|
||||
--icon-padding: 0.5rem;
|
||||
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 0.75rem;
|
||||
padding: 1.5rem 2rem 1.5rem 1.5rem;
|
||||
background-color: var(--gray-999);
|
||||
border-radius: 0 0 0.75rem 0.75rem;
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.socials {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.625rem;
|
||||
font-size: var(--icon-size);
|
||||
}
|
||||
|
||||
.social {
|
||||
display: flex;
|
||||
padding: var(--icon-padding);
|
||||
text-decoration: none;
|
||||
color: var(--accent-dark);
|
||||
transition: color var(--theme-transition);
|
||||
}
|
||||
|
||||
.social:hover,
|
||||
.social:focus {
|
||||
color: var(--accent-text-over);
|
||||
}
|
||||
|
||||
.theme-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: calc(var(--icon-size) + 2 * var(--icon-padding));
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
nav {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
align-items: center;
|
||||
padding: 2.5rem 5rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.menu-header {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.site-title {
|
||||
font-size: var(--text-lg);
|
||||
}
|
||||
|
||||
.menu-content {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.nav-items {
|
||||
position: relative;
|
||||
flex-direction: row;
|
||||
font-size: var(--text-sm);
|
||||
border-radius: 999rem;
|
||||
border: 0;
|
||||
padding: 0.5rem 0.5625rem;
|
||||
background: radial-gradient(var(--gray-900), var(--gray-800) 150%);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.nav-items::before {
|
||||
position: absolute;
|
||||
inset: -1px;
|
||||
content: '';
|
||||
background: var(--gradient-stroke);
|
||||
border-radius: 999rem;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.link {
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 999rem;
|
||||
transition:
|
||||
color var(--theme-transition),
|
||||
background-color var(--theme-transition);
|
||||
}
|
||||
|
||||
.link:hover,
|
||||
.link:focus {
|
||||
color: var(--gray-100);
|
||||
background-color: var(--accent-subtle-overlay);
|
||||
}
|
||||
|
||||
.link[aria-current='page'] {
|
||||
color: var(--accent-text-over);
|
||||
background-color: var(--accent-regular);
|
||||
}
|
||||
|
||||
.menu-footer {
|
||||
--icon-padding: 0.375rem;
|
||||
|
||||
justify-self: flex-end;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.socials {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 60em) {
|
||||
.socials {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 0;
|
||||
}
|
||||
}
|
||||
@media (forced-colors: active) {
|
||||
.link[aria-current='page'] {
|
||||
color: SelectedItem;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,17 +0,0 @@
|
|||
---
|
||||
import Card from "./Card/index.astro";
|
||||
import Pulse from "./Pulse.astro";
|
||||
---
|
||||
|
||||
<Card colSpan="md:col-span-1" rowSpan="md:row-span-2">
|
||||
<div class="flex justify-between w-full items-start mb-2">
|
||||
<div class="flex flex-col">
|
||||
<h2>Now</h2>
|
||||
<a href="https://sive.rs/nowff" target="_blank">
|
||||
<span class="text-xs text-gray-500 cursor-pointer">what's that ?</span>
|
||||
</a>
|
||||
</div>
|
||||
<Pulse />
|
||||
</div>
|
||||
<p class="text-xs">Currently studying</p>
|
||||
</Card>
|
16
src/components/Pill.astro
Normal file
|
@ -0,0 +1,16 @@
|
|||
<div class="pill"><slot /></div>
|
||||
|
||||
<style>
|
||||
.pill {
|
||||
display: flex;
|
||||
padding: 0.5rem 1rem;
|
||||
gap: 0.5rem;
|
||||
color: var(--accent-text-over);
|
||||
border: 1px solid var(--accent-regular);
|
||||
background-color: var(--accent-regular);
|
||||
border-radius: 999rem;
|
||||
font-size: var(--text-md);
|
||||
line-height: 1.35;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
64
src/components/PortfolioPreview.astro
Normal file
|
@ -0,0 +1,64 @@
|
|||
---
|
||||
import type { CollectionEntry } from 'astro:content';
|
||||
|
||||
interface Props {
|
||||
project: CollectionEntry<'work'>;
|
||||
}
|
||||
|
||||
const { data, id } = Astro.props.project;
|
||||
---
|
||||
|
||||
<a class="card" href={`/work/${id}`}>
|
||||
<span class="title">{data.title}</span>
|
||||
<img src={data.img} alt={data.img_alt || ''} loading="lazy" decoding="async" />
|
||||
</a>
|
||||
|
||||
<style>
|
||||
.card {
|
||||
display: grid;
|
||||
grid-template: auto 1fr / auto 1fr;
|
||||
height: 11rem;
|
||||
background: var(--gradient-subtle);
|
||||
border: 1px solid var(--gray-800);
|
||||
border-radius: 0.75rem;
|
||||
overflow: hidden;
|
||||
box-shadow: var(--shadow-sm);
|
||||
text-decoration: none;
|
||||
font-family: var(--font-brand);
|
||||
font-size: var(--text-lg);
|
||||
font-weight: 500;
|
||||
transition: box-shadow var(--theme-transition);
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.title {
|
||||
grid-area: 1 / 1 / 2 / 2;
|
||||
z-index: 1;
|
||||
margin: 0.5rem;
|
||||
padding: 0.5rem 1rem;
|
||||
background: var(--gray-999);
|
||||
color: var(--gray-200);
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
img {
|
||||
grid-area: 1 / 1 / 3 / 3;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
.card {
|
||||
height: 22rem;
|
||||
border-radius: 1.5rem;
|
||||
}
|
||||
|
||||
.title {
|
||||
border-radius: 0.9375rem;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,6 +0,0 @@
|
|||
<span class="relative flex h-3 w-3">
|
||||
<span
|
||||
class="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"
|
||||
></span>
|
||||
<span class="relative inline-flex rounded-full h-3 w-3 bg-green-500"></span>
|
||||
</span>
|
57
src/components/Skills.astro
Normal file
|
@ -0,0 +1,57 @@
|
|||
---
|
||||
import Icon from './Icon.astro';
|
||||
---
|
||||
|
||||
<section class="box skills">
|
||||
<div class="stack gap-2 lg:gap-4">
|
||||
<Icon icon="terminal-window" color="var(--accent-regular)" size="2.5rem" gradient />
|
||||
<h2>Discord bot dev</h2>
|
||||
<p>I have been working on a Discord bot called Ellie since 2018.</p>
|
||||
</div>
|
||||
<div class="stack gap-2 lg:gap-4">
|
||||
<Icon icon="trophy" color="var(--accent-regular)" size="2.5rem" gradient />
|
||||
<h2>Systems administrator</h2>
|
||||
<p>I am a systems administrator and have been working on my skills since 2021.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.box {
|
||||
border: 1px solid var(--gray-800);
|
||||
border-radius: 0.75rem;
|
||||
padding: 1.5rem;
|
||||
background-color: var(--gray-999_40);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.skills {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3rem;
|
||||
}
|
||||
|
||||
.skills h2 {
|
||||
font-size: var(--text-lg);
|
||||
}
|
||||
|
||||
.skills p {
|
||||
color: var(--gray-400);
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
.box {
|
||||
border-radius: 1.5rem;
|
||||
padding: 2.5rem;
|
||||
}
|
||||
|
||||
.skills {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 5rem;
|
||||
}
|
||||
|
||||
.skills h2 {
|
||||
font-size: var(--text-2xl);
|
||||
}
|
||||
}
|
||||
</style>
|
95
src/components/ThemeToggle.astro
Normal file
|
@ -0,0 +1,95 @@
|
|||
---
|
||||
import Icon from './Icon.astro';
|
||||
---
|
||||
|
||||
<theme-toggle>
|
||||
<button>
|
||||
<span class="sr-only">Dark theme</span>
|
||||
<span class="icon light"><Icon icon="sun" /></span>
|
||||
<span class="icon dark"><Icon icon="moon-stars" /></span>
|
||||
</button>
|
||||
</theme-toggle>
|
||||
|
||||
<style>
|
||||
button {
|
||||
display: flex;
|
||||
border: 0;
|
||||
border-radius: 999rem;
|
||||
padding: 0;
|
||||
background-color: var(--gray-999);
|
||||
box-shadow: inset 0 0 0 1px var(--accent-overlay);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.icon {
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
display: flex;
|
||||
padding: 0.5rem;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
font-size: 1rem;
|
||||
color: var(--accent-overlay);
|
||||
}
|
||||
|
||||
.icon.light::before {
|
||||
content: '';
|
||||
z-index: -1;
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-color: var(--accent-regular);
|
||||
border-radius: 999rem;
|
||||
}
|
||||
|
||||
:global(.theme-dark) .icon.light::before {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
:global(.theme-dark) .icon.dark,
|
||||
:global(html:not(.theme-dark)) .icon.light,
|
||||
button[aria-pressed='false'] .icon.light {
|
||||
color: var(--accent-text-over);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.icon,
|
||||
.icon.light::before {
|
||||
transition:
|
||||
transform var(--theme-transition),
|
||||
color var(--theme-transition);
|
||||
}
|
||||
}
|
||||
|
||||
@media (forced-colors: active) {
|
||||
.icon.light::before {
|
||||
background-color: SelectedItem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
class ThemeToggle extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
const button = this.querySelector('button')!;
|
||||
|
||||
/** Set the theme to dark/light mode. */
|
||||
const setTheme = (dark: boolean) => {
|
||||
document.documentElement.classList[dark ? 'add' : 'remove']('theme-dark');
|
||||
button.setAttribute('aria-pressed', String(dark));
|
||||
};
|
||||
|
||||
// Toggle the theme when a user clicks the button.
|
||||
button.addEventListener('click', () => setTheme(!this.isDark()));
|
||||
|
||||
// Initialize button state to reflect current theme.
|
||||
setTheme(this.isDark());
|
||||
}
|
||||
|
||||
isDark() {
|
||||
return document.documentElement.classList.contains('theme-dark');
|
||||
}
|
||||
}
|
||||
customElements.define('theme-toggle', ThemeToggle);
|
||||
</script>
|
|
@ -1,39 +0,0 @@
|
|||
---
|
||||
import { getCurrentTimeInItaly, formatTimeForItaly } from "../lib/helpers";
|
||||
import Card from "./Card/index.astro";
|
||||
---
|
||||
|
||||
<script>
|
||||
import { onCleanup, onMount } from "solid-js";
|
||||
import { formatTimeForItaly } from "../lib/helpers";
|
||||
|
||||
let interval: ReturnType<typeof setInterval>;
|
||||
|
||||
function updateClock() {
|
||||
const timeDisplay = document.getElementById("timeDisplay");
|
||||
const now = new Date();
|
||||
|
||||
if (timeDisplay) {
|
||||
timeDisplay.textContent = formatTimeForItaly(now);
|
||||
timeDisplay.setAttribute("datetime", now.toISOString());
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
interval = setInterval(updateClock, 1000);
|
||||
});
|
||||
|
||||
onCleanup(() => {
|
||||
clearInterval(interval);
|
||||
});
|
||||
</script>
|
||||
|
||||
<Card colSpan="lg:col-span-2" rowSpan="md:row-span-2" title="Time zone">
|
||||
<time
|
||||
datetime=""
|
||||
id="timeDisplay"
|
||||
class="text-2xl xl:text-5xl xl:whitespace-nowrap w-50 xl:w-100 h-[calc(100%-28px)] font-serif flex justify-center items-center"
|
||||
>
|
||||
{formatTimeForItaly(getCurrentTimeInItaly())}
|
||||
</time>
|
||||
</Card>
|
|
@ -1,79 +0,0 @@
|
|||
import { type JSX, Show, createSignal } from "solid-js";
|
||||
|
||||
type Props = {
|
||||
children: JSX.Element;
|
||||
};
|
||||
|
||||
function Tooltip(props: Props) {
|
||||
const [isVisible, setIsVisible] = createSignal(false);
|
||||
const [clickCount, setClickCount] = createSignal(0);
|
||||
|
||||
const messages = [
|
||||
"Hi there!",
|
||||
"Clicked again?",
|
||||
"Still here?",
|
||||
"Persistent, aren't you?",
|
||||
"What's up?",
|
||||
"Again? Really?",
|
||||
"You're curious!",
|
||||
"Not cool!",
|
||||
"Give it a break!",
|
||||
"That's annoying!",
|
||||
"Hands off!",
|
||||
"No more clicks!",
|
||||
"Seriously?!",
|
||||
"Ouch! That hurts!",
|
||||
"You're persistent!",
|
||||
"Why the curiosity?",
|
||||
"I'm getting tired!",
|
||||
"I'm bored!",
|
||||
"Enough's enough!",
|
||||
"Find another hobby!",
|
||||
"Stop, please!",
|
||||
"Okay, last one!",
|
||||
"That's it, I'm done!",
|
||||
];
|
||||
|
||||
const currentMessage = () => {
|
||||
const count = clickCount();
|
||||
if (count >= messages.length) {
|
||||
return messages[messages.length - 1];
|
||||
}
|
||||
return messages[count];
|
||||
};
|
||||
|
||||
return (
|
||||
<div class="relative inline-block">
|
||||
<div
|
||||
onMouseDown={() => {
|
||||
setIsVisible(!isVisible());
|
||||
if (isVisible()) {
|
||||
setClickCount((count) => count + 1);
|
||||
}
|
||||
}}
|
||||
onMouseUp={() => {
|
||||
setIsVisible(false);
|
||||
}}
|
||||
onTouchStart={() => {
|
||||
setIsVisible(!isVisible());
|
||||
if (isVisible()) {
|
||||
setClickCount((count) => count + 1);
|
||||
}
|
||||
}}
|
||||
onTouchEnd={() => {
|
||||
setIsVisible(false);
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
|
||||
<Show when={isVisible()}>
|
||||
<div class="absolute left-1/2 -translate-x-1/2 -translate-y-24 mt-1 w-auto max-h-[70px] p-2 bg-black text-white text-center rounded-lg z-10 shadow-custom shadow-primary-500 border border-primary-500 whitespace-normal after:content-[''] after:block after:rotate-45 after:w-4 after:h-4 after:shadow-custom after:shadow-primary-500 after:absolute after:-bottom-2 after:-translate-x-1/2 after:left-1/2 after:bg-black after:z-20">
|
||||
<p class="w-max">{currentMessage()}</p>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Tooltip;
|
17
src/content.config.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { glob } from 'astro/loaders';
|
||||
import { defineCollection, z } from 'astro:content';
|
||||
|
||||
export const collections = {
|
||||
work: defineCollection({
|
||||
// Load Markdown files in the src/content/work directory.
|
||||
loader: glob({ base: './src/content/work', pattern: '**/*.md', }),
|
||||
schema: z.object({
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
publishDate: z.coerce.date(),
|
||||
tags: z.array(z.string()),
|
||||
img: z.string(),
|
||||
img_alt: z.string().optional(),
|
||||
}),
|
||||
}),
|
||||
};
|
|
@ -1,39 +0,0 @@
|
|||
---
|
||||
layout: ../../layouts/LayoutBlogPost.astro
|
||||
title: "New projects"
|
||||
description: "The projects I have started."
|
||||
pubDate: 2023-12-09
|
||||
category: "updates"
|
||||
---
|
||||
|
||||
Hi there, it has been a while.
|
||||
|
||||
I am back to let you all know how life has been treating me so far as well as share the projects I have been working on.
|
||||
|
||||
### First up is Valkyriecoms,
|
||||
|
||||
Valkyriecoms is a social site I have been working on since December last year and just got things fully up, below is a screenshot of the site.
|
||||
|
||||
![](https://cdn.discordapp.com/attachments/1138770664342441984/1182117160609386650/image.png)
|
||||
|
||||
### Next up is Dragon's child hosting
|
||||
|
||||
Dragon's child hosting is a small server hosting service I started under the Dragon's child studios name right now we are just starting out on our journey.
|
||||
|
||||
Below is a screenshot of our panel (there is a new one which will replace this in the works)
|
||||
|
||||
![](https://cdn.discordapp.com/attachments/881396607218753607/1182651077384994816/dashboard.png)
|
||||
|
||||
### Last up is Toastielab
|
||||
|
||||
Toastielab is a small git platform that I operate wit the help of Dragon's child studios which is where I host all my projects (even the one which this site is located at)
|
||||
|
||||
I am going to include a screenshot of Toastielab below (our icons a little bit bugged right now so it is currently showing the default Forgejo icons)
|
||||
|
||||
![](https://cdn.discordapp.com/attachments/881396607218753607/1182653249480835163/image.png)
|
||||
|
||||
### Here is a list of links for the projects listed above
|
||||
|
||||
- https://valkyriecoms.com
|
||||
- https://dragonschildhosting.net
|
||||
- https://toastielab.dev
|
|
@ -1,15 +0,0 @@
|
|||
---
|
||||
layout: ../../layouts/LayoutBlogPost.astro
|
||||
title: "Update on things"
|
||||
description: "Where I have been among other things."
|
||||
pubDate: 2023-06-21
|
||||
category: "updates"
|
||||
---
|
||||
|
||||
Hey guys it has been a while and I have not streamed in a little while and I want to update you on why this has happened, First of all I have been having somputer issues as well as some IRL stuff taking over a lot, Secondly I have started some major projects, one with a friend and another to support me first project. These projects are as follows [Valkyriecoms](https://valkyriecoms.com), [Toastielab](https://toastielab.dev) and last but not least a small group stared by me and a good friend who you may know by Elearu called [Dragon's Child Studios](https://dragonschildstudios.com).
|
||||
|
||||
As you can see I have been quite busy but I hope I will be able to return to streaming once everything starts to quiet down a little.
|
||||
|
||||
Thanks for taking the time to read this,
|
||||
|
||||
Toastie_t0ast
|
|
@ -1,8 +0,0 @@
|
|||
import { defineCollection } from 'astro:content';
|
||||
import { rssSchema } from '@astrojs/rss';
|
||||
|
||||
const blog = defineCollection({
|
||||
schema: rssSchema,
|
||||
});
|
||||
|
||||
export const collections = { blog };
|
26
src/content/work/dcs.md
Normal file
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
title: Dragon's child studios
|
||||
publishDate: 2024-12-01 00:00:00
|
||||
img: /assets/dcs.png
|
||||
img_alt: A picture of my the Dragon's child studios cover logo.
|
||||
description: |
|
||||
A small group of people who like to make stuff.
|
||||
tags:
|
||||
- Game dev
|
||||
- Services
|
||||
---
|
||||
|
||||
Dragon's child studios is a group of friends who came together through our love of games and we want to try and make games that people will love.
|
||||
|
||||
Our founders are myself and [Azrael Indrason](http://www.twitch.tv/azrael_indrason)
|
||||
|
||||
Our values are:
|
||||
|
||||
- Privacy Based:
|
||||
We do not like our data being sold so why should we sell your data off to other companies?
|
||||
|
||||
- Have fun making games:
|
||||
We try to have fun in all that we do. We love games and we love to play them. So why should we make games that arent fun?
|
||||
|
||||
- Honesty and transparency:
|
||||
If we have any privacy issues we will make sure that they are resolved and that a transparency report is made.
|
13
src/content/work/ellie-bot.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
title: EllieBot
|
||||
publishDate: 2024-12-01 00:00:00
|
||||
img: /assets/ellie-casey.png
|
||||
img_alt: A picture of my OC Ellie who also acts as the mascot for the EllieBot project.
|
||||
description: |
|
||||
A small little Discord bot written in C#
|
||||
tags:
|
||||
- Dev
|
||||
- Discord
|
||||
---
|
||||
|
||||
EllieBot is a Discord bot that I have been working on since 2018 and she is currently on version 5.3.2 and her code is currently located at https://toastielab.dev/Emotions-stuff/elliebot
|
13
src/content/work/valkyriecoms.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
title: Valkyriecoms
|
||||
publishDate: 2024-12-01 00:00:00
|
||||
img: /assets/valkyriecoms.png
|
||||
img_alt: A picture of my the valkyriecoms cover logo.
|
||||
description: |
|
||||
A safe place for people to hang out with others.
|
||||
tags:
|
||||
- Social media
|
||||
- Services
|
||||
---
|
||||
|
||||
Valkyriecoms is a social site I have been working on since December 2022.
|
12
src/env.d.ts
vendored
|
@ -1,12 +0,0 @@
|
|||
/// <reference path="../.astro/types.d.ts" />
|
||||
/// <reference types="astro/client" />
|
||||
interface ImportMetaEnv {
|
||||
readonly CONTENTFUL_SPACE_ID: string;
|
||||
readonly CONTENTFUL_DELIVERY_TOKEN: string;
|
||||
readonly CONTENTFUL_PREVIEW_TOKEN: string;
|
||||
}
|
||||
|
||||
declare module "*.riv" {
|
||||
const content: any;
|
||||
export default content;
|
||||
}
|
84
src/layouts/BaseLayout.astro
Normal file
|
@ -0,0 +1,84 @@
|
|||
---
|
||||
// Learn about using Astro layouts:
|
||||
// https://docs.astro.build/en/core-concepts/layouts/
|
||||
|
||||
// Component Imports
|
||||
import MainHead from '../components/MainHead.astro';
|
||||
import Nav from '../components/Nav.astro';
|
||||
import Footer from '../components/Footer.astro';
|
||||
|
||||
interface Props {
|
||||
title?: string | undefined;
|
||||
description?: string | undefined;
|
||||
}
|
||||
|
||||
const { title, description } = Astro.props;
|
||||
---
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<MainHead title={title} description={description} />
|
||||
</head>
|
||||
<body>
|
||||
<div class="stack backgrounds">
|
||||
<Nav />
|
||||
<slot />
|
||||
<Footer />
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Add “loaded” class once the document has completely loaded.
|
||||
addEventListener('load', () => document.documentElement.classList.add('loaded'));
|
||||
</script>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--_placeholder-bg: linear-gradient(transparent, transparent);
|
||||
--bg-image-main: url('/assets/backgrounds/bg-main-light-800w.jpg');
|
||||
--bg-image-main-curves: url('/assets/backgrounds/bg-main-light.svg');
|
||||
--bg-image-subtle-1: var(--_placeholder-bg);
|
||||
--bg-image-subtle-2: var(--_placeholder-bg);
|
||||
--bg-image-footer: var(--_placeholder-bg);
|
||||
--bg-svg-blend-mode: overlay;
|
||||
--bg-blend-mode: darken;
|
||||
--bg-image-aspect-ratio: 2.25;
|
||||
--bg-scale: 1.68;
|
||||
--bg-aspect-ratio: calc(var(--bg-image-aspect-ratio) / var(--bg-scale));
|
||||
--bg-gradient-size: calc(var(--bg-scale) * 100%);
|
||||
}
|
||||
|
||||
:root.theme-dark {
|
||||
--bg-image-main: url('/assets/backgrounds/bg-main-dark-800w.jpg');
|
||||
--bg-image-main-curves: url('/assets/backgrounds/bg-main-dark.svg');
|
||||
--bg-svg-blend-mode: darken;
|
||||
--bg-blend-mode: lighten;
|
||||
}
|
||||
|
||||
.backgrounds {
|
||||
min-height: 100%;
|
||||
isolation: isolate;
|
||||
background:
|
||||
/*noise*/
|
||||
url('/assets/backgrounds/noise.png') top center/220px repeat,
|
||||
/*footer*/ var(--bg-image-footer) bottom center/var(--bg-gradient-size) no-repeat,
|
||||
/*header1*/ var(--bg-image-main-curves) top center/var(--bg-gradient-size) no-repeat,
|
||||
/*header2*/ var(--bg-image-main) top center/var(--bg-gradient-size) no-repeat,
|
||||
/*base*/ var(--gray-999);
|
||||
background-blend-mode: /*noise*/
|
||||
overlay,
|
||||
/*footer*/ var(--bg-blend-mode),
|
||||
/*header1*/ var(--bg-svg-blend-mode),
|
||||
/*header2*/ normal,
|
||||
/*base*/ normal;
|
||||
}
|
||||
@media (forced-colors: active) {
|
||||
/* Deactivate custom backgrounds for high contrast users. */
|
||||
.backgrounds {
|
||||
background: none;
|
||||
background-blend-mode: none;
|
||||
--bg-gradient-size: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
</html>
|
|
@ -1,141 +0,0 @@
|
|||
---
|
||||
interface Props {
|
||||
title: string;
|
||||
description: string;
|
||||
page?: "blog";
|
||||
slug?: string | undefined;
|
||||
frontmatter?: {
|
||||
file: string;
|
||||
url: string | undefined;
|
||||
} & {
|
||||
title: string;
|
||||
description: string;
|
||||
pubDate: string;
|
||||
minutesRead: string;
|
||||
};
|
||||
}
|
||||
|
||||
const { title, description, page, slug, frontmatter } = Astro.props;
|
||||
|
||||
const schema =
|
||||
page !== "blog"
|
||||
? {
|
||||
"@context": "http://schema.org",
|
||||
"@type": "Person",
|
||||
name: "Toastiet0ast",
|
||||
url: "https://toastiet0ast.com",
|
||||
sameAs: [
|
||||
"https://toastielab.dev/toastie_t0ast",
|
||||
],
|
||||
jobTitle: "Freelance Systems Administrator",
|
||||
worksFor: {
|
||||
"@type": "Organization",
|
||||
name: "Self-Employed",
|
||||
address: {
|
||||
"@type": "PostalAddress",
|
||||
addressLocality: "New Zealand",
|
||||
addressCountry: "NZ",
|
||||
},
|
||||
},
|
||||
nationality: {
|
||||
"@type": "Country",
|
||||
name: "New Zealand",
|
||||
},
|
||||
}
|
||||
: {
|
||||
"@context": "http://schema.org",
|
||||
"@type": "BlogPosting",
|
||||
mainEntityOfPage: {
|
||||
"@type": "WebPage",
|
||||
"@id": `https://toastiet0ast.com/blog/${slug}`,
|
||||
},
|
||||
headline: frontmatter?.title || title,
|
||||
description: frontmatter?.description || title,
|
||||
author: {
|
||||
"@type": "Person",
|
||||
name: "Toastie_t0ast",
|
||||
url: "https://toastiet0ast.com",
|
||||
sameAs: [
|
||||
"https://toastielab.dev/toastie_t0ast",
|
||||
],
|
||||
},
|
||||
publisher: {
|
||||
"@type": "Organization",
|
||||
name: "toastiet0ast",
|
||||
},
|
||||
datePublished: frontmatter?.pubDate || new Date().toISOString(),
|
||||
dateModified: frontmatter?.pubDate || new Date().toISOString(),
|
||||
};
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<title>{title}</title>
|
||||
<meta name="description" content={description} />
|
||||
<meta name="robots" content="/favicon/sitemap-index.xml" />
|
||||
<!-- Basic OG tags for sharing your website's content on platforms like Facebook and LinkedIn -->
|
||||
<meta property="og:title" content={title} />
|
||||
<meta property="og:description" content={description} />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content={Astro.url.origin} />
|
||||
<!-- Basic Twitter Card tags -->
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content={title} />
|
||||
<meta name="twitter:description" content={description} />
|
||||
|
||||
<link rel="preconnect" href="https://cdn.fontshare.com" />
|
||||
<script type="application/ld+json" set:html={JSON.stringify(schema)} />
|
||||
</head>
|
||||
<body
|
||||
class="bg-darkslate-700 md:h-screen flex flex-col justify-center items-center"
|
||||
>
|
||||
<slot name="loader" />
|
||||
<slot />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<style is:global>
|
||||
@font-face {
|
||||
font-family: "CabinetGrotesk";
|
||||
src: url("/fonts/CabinetGrotesk-Variable.ttf") format("truetype-variations");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
font-weight: 100 1000;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Satoshi";
|
||||
src: url("/fonts/Satoshi-Variable.ttf") format("truetype-variations");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
font-weight: 100 1000;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: "Satoshi", sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: "Cabinet Grotesk", sans-serif;
|
||||
}
|
||||
p {
|
||||
font-family: "Satoshi";
|
||||
}
|
||||
|
||||
b {
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
</style>
|
|
@ -1,21 +0,0 @@
|
|||
---
|
||||
import BasicLayout from "./BasicLayout.astro";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
description: string;
|
||||
page?: "travel";
|
||||
fullScreen?: string;
|
||||
}
|
||||
|
||||
const { title, description, page, fullScreen } = Astro.props;
|
||||
---
|
||||
|
||||
<BasicLayout title={title} description={description}>
|
||||
<div
|
||||
slot="loader"
|
||||
class="loader bg-darkslate-700 text-neutral-50 text-3xl font-black uppercase flex justify-center items-center w-screen h-screen z-50 fixed top-0 bottom-0 right-0 left-0"
|
||||
>
|
||||
</div>
|
||||
<slot />
|
||||
</BasicLayout>
|
|
@ -1,41 +0,0 @@
|
|||
---
|
||||
import { formatDate } from "../lib/helpers";
|
||||
import type { MarkdownLayoutProps } from "astro";
|
||||
import BasicLayout from "./BasicLayout.astro";
|
||||
|
||||
type Props = MarkdownLayoutProps<{
|
||||
title: string;
|
||||
description: string;
|
||||
pubDate: string;
|
||||
minutesRead: string;
|
||||
}>;
|
||||
|
||||
const { slug } = Astro.params;
|
||||
const { frontmatter } = Astro.props;
|
||||
---
|
||||
|
||||
<BasicLayout
|
||||
frontmatter={frontmatter}
|
||||
slug={slug}
|
||||
page="blog"
|
||||
title={`Toastie_t0ast - ${frontmatter.title}`}
|
||||
description={frontmatter.description}
|
||||
>
|
||||
<main class="mx-auto max-w-3xl w-full h-screen p-8 text-neutral-100">
|
||||
<a
|
||||
href="/blog"
|
||||
class="text-white bg-neutral-900 hover:bg-neutral-800 px-4 py-2 border-1 border-solid border-neutral-600 rounded-lg mb-8"
|
||||
>Back</a
|
||||
>
|
||||
<div class="my-10">
|
||||
<h1 class="text-5xl font-semibold">{frontmatter.title}</h1>
|
||||
<div class="flex justify-between pt-4 text-gray-500 text-sm">
|
||||
<p>{formatDate(new Date(frontmatter.pubDate))}</p>
|
||||
<p>{frontmatter.minutesRead}</p>
|
||||
</div>
|
||||
</div>
|
||||
<article class="prose prose-p:text-red-500 prose-slate prose-invert">
|
||||
<slot />
|
||||
</article>
|
||||
</main>
|
||||
</BasicLayout>
|
|
@ -1,12 +0,0 @@
|
|||
import getReadingTime from "reading-time";
|
||||
import { toString } from "mdast-util-to-string";
|
||||
|
||||
export function remarkReadingTime() {
|
||||
return function (tree, { data }) {
|
||||
const textOnPage = toString(tree);
|
||||
const readingTime = getReadingTime(textOnPage);
|
||||
// readingTime.text will give us minutes read as a friendly string,
|
||||
// i.e. "3 min read"
|
||||
data.astro.frontmatter.minutesRead = readingTime.text;
|
||||
};
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
export const LINKS = {
|
||||
toastielab: "https://toastielab.dev/toastie_t0ast",
|
||||
discord: "https://discordapp.com/users/234542843732033537",
|
||||
valkyriecoms: "https://valkyriecoms.com/@toastie",
|
||||
};
|
||||
|
||||
export const loaderAnimation = [
|
||||
".loader",
|
||||
{ opacity: [1, 0], pointerEvents: "none" },
|
||||
{ easing: "ease-out" },
|
||||
];
|
|
@ -1,40 +0,0 @@
|
|||
export function trimText(input: string, maxLength: number = 100): string {
|
||||
if (input.length <= maxLength) return input;
|
||||
return input.substring(0, maxLength - 3) + "...";
|
||||
}
|
||||
export function getCurrentTimeInItaly(): Date {
|
||||
// Create a date object with the current UTC time
|
||||
const now = new Date();
|
||||
|
||||
// Convert the UTC time to Italy's time
|
||||
const offsetItaly = 2; // Italy is in Central European Summer Time (UTC+2), but you might need to adjust this based on Daylight Saving Time
|
||||
now.setHours(now.getUTCHours() + offsetItaly);
|
||||
|
||||
return now;
|
||||
}
|
||||
|
||||
export function formatTimeForItaly(date: Date): string {
|
||||
const options: Intl.DateTimeFormatOptions = {
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
hour12: true, // This will format the time in 12-hour format with AM/PM
|
||||
timeZone: "Pacific/Auckland",
|
||||
};
|
||||
|
||||
let formattedTime = new Intl.DateTimeFormat("en-US", options).format(date);
|
||||
|
||||
// Append the time zone abbreviation. You can automate this with libraries like `moment-timezone`.
|
||||
// For simplicity, here I'm just appending "CET", but do remember that Italy switches between CET and CEST.
|
||||
formattedTime += " NZST";
|
||||
|
||||
return formattedTime;
|
||||
}
|
||||
|
||||
export function formatDate(date: Date): string {
|
||||
return date.toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
});
|
||||
}
|
8
src/pages/404.astro
Normal file
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
import Hero from '../components/Hero.astro';
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
---
|
||||
|
||||
<BaseLayout title="Not Found" description="404 Error — this page was not found">
|
||||
<Hero title="Page Not Found" tagline="Not found" />
|
||||
</BaseLayout>
|
99
src/pages/about.astro
Normal file
|
@ -0,0 +1,99 @@
|
|||
---
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
|
||||
import ContactCTA from '../components/ContactCTA.astro';
|
||||
import Hero from '../components/Hero.astro';
|
||||
---
|
||||
|
||||
<BaseLayout title="About | Toastie_t0ast" description="About Toastie_t0ast">
|
||||
<div class="stack gap-20">
|
||||
<main class="wrapper about">
|
||||
<Hero
|
||||
title="About"
|
||||
tagline="Thanks for stopping by. Read below to learn more about myself and my background."
|
||||
>
|
||||
</Hero>
|
||||
|
||||
<section>
|
||||
<h2 class="section-title">Background</h2>
|
||||
<div class="content">
|
||||
<p>
|
||||
Hi there I am Toastie a software developer from New Zealand I mostly write Discord bots and some other things.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title">Education</h2>
|
||||
<div class="content">
|
||||
<p>Studied at Learner Me here in NZ.</p>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title">Skills</h2>
|
||||
<div class="content">
|
||||
<p>Discord bot development and systems administration</p>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<ContactCTA />
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
||||
<style>
|
||||
.about {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3.5rem;
|
||||
}
|
||||
|
||||
img {
|
||||
margin-top: 1.5rem;
|
||||
border-radius: 1.5rem;
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
color: var(--gray-200);
|
||||
}
|
||||
|
||||
.section-title {
|
||||
grid-column-start: 1;
|
||||
font-size: var(--text-xl);
|
||||
color: var(--gray-0);
|
||||
}
|
||||
|
||||
.content {
|
||||
grid-column: 2 / 4;
|
||||
}
|
||||
|
||||
.content :global(a) {
|
||||
text-decoration: 1px solid underline transparent;
|
||||
text-underline-offset: 0.25em;
|
||||
transition: text-decoration-color var(--theme-transition);
|
||||
}
|
||||
|
||||
.content :global(a:hover),
|
||||
.content :global(a:focus) {
|
||||
text-decoration-color: currentColor;
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
.about {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 60% 1fr;
|
||||
}
|
||||
|
||||
.about > :global(:first-child) {
|
||||
grid-column-start: 2;
|
||||
}
|
||||
|
||||
section {
|
||||
display: contents;
|
||||
font-size: var(--text-lg);
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,129 +0,0 @@
|
|||
---
|
||||
import { getCollection } from "astro:content";
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return (await getCollection("blog")).map(({ slug }) => ({
|
||||
params: { slug },
|
||||
}));
|
||||
}
|
||||
|
||||
const { slug } = Astro.params;
|
||||
|
||||
if (slug === undefined) {
|
||||
throw new Error("slug is missing");
|
||||
}
|
||||
|
||||
const posts = (await getCollection("blog")).sort(
|
||||
(blogEntryA, blogEntryB) =>
|
||||
(blogEntryB.data.pubDate || new Date()).getTime() -
|
||||
(blogEntryA.data.pubDate || new Date()).getTime()
|
||||
);
|
||||
|
||||
const entry = posts.find((entry) => entry.slug === slug);
|
||||
if (entry === undefined) {
|
||||
return Astro.redirect("/404");
|
||||
}
|
||||
|
||||
const { Content } = await entry.render();
|
||||
---
|
||||
|
||||
<Content />
|
||||
|
||||
<style is:global>
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #1a0dab;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
margin-left: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 20px 0;
|
||||
padding-left: 15px;
|
||||
border-left: 5px solid #ccc;
|
||||
}
|
||||
|
||||
pre,
|
||||
code {
|
||||
font-family: "Courier New", monospace;
|
||||
background-color: #f4f4f4;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 10px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
code {
|
||||
padding: 2px 4px;
|
||||
font-size: 90%;
|
||||
color: #d63384;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
text-align: left;
|
||||
padding: 8px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
</style>
|
|
@ -1,47 +0,0 @@
|
|||
---
|
||||
import { getCollection } from "astro:content";
|
||||
import Layout from "../../layouts/Layout.astro";
|
||||
import PostRow from "../../components/Blog/PostRow.astro";
|
||||
|
||||
const posts = (await getCollection("blog"))?.sort(
|
||||
(blogEntryA, blogEntryB) =>
|
||||
(blogEntryB.data.pubDate || new Date()).getTime() -
|
||||
(blogEntryA.data.pubDate || new Date()).getTime()
|
||||
);
|
||||
---
|
||||
|
||||
<script>
|
||||
import { timeline, type TimelineDefinition } from "motion";
|
||||
import { loaderAnimation } from "../../lib/constants";
|
||||
|
||||
const sequence = [loaderAnimation];
|
||||
|
||||
timeline(sequence as TimelineDefinition);
|
||||
</script>
|
||||
|
||||
<Layout
|
||||
title="Toastie_t0ast - Blog"
|
||||
description="I'm a developer based in New Zealand, I like to make Discord Bots as well as work on game servers through my host https://dragonschildhosting.net."
|
||||
>
|
||||
<main
|
||||
class="w-screen h-screen flex flex-col justify-start items-start max-w-3xl mx-auto p-8"
|
||||
>
|
||||
<a
|
||||
href="/"
|
||||
class="text-white bg-neutral-900 hover:bg-neutral-800 px-4 py-2 mb-8 border-1 border-solid border-neutral-600 rounded-lg"
|
||||
>Back</a
|
||||
>
|
||||
<h1 class="text-4xl font-bold mb-4 text-neutral-100">Posts</h1>
|
||||
<ul class="w-full">
|
||||
{
|
||||
posts?.map((post) => (
|
||||
<PostRow
|
||||
title={post.data.title || "No title"}
|
||||
date={post.data.pubDate || new Date()}
|
||||
url={post.slug}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</main>
|
||||
</Layout>
|
|
@ -1,61 +1,221 @@
|
|||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
import Card from "../components/Card/index.astro";
|
||||
import IntroCard from "../components/IntroCard.astro";
|
||||
import ContactsCard from "../components/ContactsCard.astro";
|
||||
import TimeZone from "../components/TimeZoneCard.astro";
|
||||
import AboutMe from "../components/AboutMe.astro";
|
||||
import Now from "../components/Now.astro";
|
||||
import { getCollection } from 'astro:content';
|
||||
|
||||
// Layout import — provides basic page elements: <head>, <nav>, <footer> etc.
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
|
||||
// Component Imports
|
||||
import CallToAction from '../components/CallToAction.astro';
|
||||
import Grid from '../components/Grid.astro';
|
||||
import Hero from '../components/Hero.astro';
|
||||
import Icon from '../components/Icon.astro';
|
||||
import Pill from '../components/Pill.astro';
|
||||
import PortfolioPreview from '../components/PortfolioPreview.astro';
|
||||
|
||||
// Page section components
|
||||
import ContactCTA from '../components/ContactCTA.astro';
|
||||
import Skills from '../components/Skills.astro';
|
||||
|
||||
// Content Fetching: List four most recent work projects
|
||||
const projects = (await getCollection('work'))
|
||||
.sort((a, b) => b.data.publishDate.valueOf() - a.data.publishDate.valueOf())
|
||||
.slice(0, 4);
|
||||
|
||||
// Full Astro Component Syntax:
|
||||
// https://docs.astro.build/basics/astro-components/
|
||||
---
|
||||
|
||||
<script>
|
||||
import { stagger, spring, timeline, type TimelineDefinition } from "motion";
|
||||
import { loaderAnimation } from "../lib/constants";
|
||||
const cards = document.querySelectorAll(".card");
|
||||
<BaseLayout>
|
||||
<div class="stack gap-20 lg:gap-48">
|
||||
<div class="wrapper stack gap-8 lg:gap-20">
|
||||
<header class="hero">
|
||||
<Hero
|
||||
title="Hello, my name is Toastie_t0ast"
|
||||
tagline="I am a systems admin and a Discord bot developer."
|
||||
align="start"
|
||||
>
|
||||
<div class="roles">
|
||||
<Pill><Icon icon="code" size="1.33em" /> Developer</Pill>
|
||||
</div>
|
||||
</Hero>
|
||||
</header>
|
||||
|
||||
const sequence = [
|
||||
loaderAnimation,
|
||||
[
|
||||
cards,
|
||||
{ y: ["40%", "0%"], opacity: [0, 1] },
|
||||
{
|
||||
at: "-0.1",
|
||||
duration: 0.4,
|
||||
delay: stagger(0.3),
|
||||
easing: spring({ velocity: 100, stiffness: 50, damping: 10 }),
|
||||
},
|
||||
],
|
||||
];
|
||||
<Skills />
|
||||
</div>
|
||||
|
||||
timeline(sequence as TimelineDefinition);
|
||||
</script>
|
||||
<main class="wrapper stack gap-20 lg:gap-48">
|
||||
<section class="section with-background with-cta">
|
||||
<header class="section-header stack gap-2 lg:gap-4">
|
||||
<h3>Selected Work</h3>
|
||||
<p>Take a look below at some of the work I have worked on over the past few years.</p>
|
||||
</header>
|
||||
|
||||
<Layout
|
||||
title="Toastie_t0ast - A random dev"
|
||||
description="I'm a developer based in New Zealand, I like to make Discord Bots as well as work on game servers through my host https://dragonschildhosting.net."
|
||||
>
|
||||
<main
|
||||
class="text-white m-auto p-2 grid gap-2 max-w-6xl overflow-hidden relative w-full sm:p-4 sm:gap-2 md:grid-cols-2 md:gap-3 md:p-6 lg:h-screen lg:grid-rows-8 lg:grid-cols-4 lg:gap-4 lg:max-h-[800px]"
|
||||
>
|
||||
<IntroCard />
|
||||
<AboutMe />
|
||||
<ContactsCard />
|
||||
<TimeZone />
|
||||
<Now />
|
||||
<Card
|
||||
colSpan="md:col-span-1"
|
||||
rowSpan="md:row-span-2 flex gap-4"
|
||||
title="Blog"
|
||||
href="/blog"
|
||||
/>
|
||||
<Card colSpan="md:col-span-1" rowSpan="md:row-span-1">
|
||||
<p class="text-xs">
|
||||
© 2024 · Crafted with ♥️ using <a
|
||||
href="https://astro.build/"
|
||||
target="_blank"
|
||||
class="text-red-500">Astro</a
|
||||
> by Toastie_t0ast.
|
||||
</p>
|
||||
</Card>
|
||||
</main>
|
||||
</Layout>
|
||||
<div class="gallery">
|
||||
<Grid variant="offset">
|
||||
{
|
||||
projects.map((project) => (
|
||||
<li>
|
||||
<PortfolioPreview project={project} />
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</Grid>
|
||||
</div>
|
||||
|
||||
<div class="cta">
|
||||
<CallToAction href="/work/">
|
||||
View All
|
||||
<Icon icon="arrow-right" size="1.2em" />
|
||||
</CallToAction>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<ContactCTA />
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
||||
<style>
|
||||
.hero {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.roles {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hero img {
|
||||
aspect-ratio: 5 / 4;
|
||||
object-fit: cover;
|
||||
object-position: top;
|
||||
border-radius: 1.5rem;
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
.hero {
|
||||
display: grid;
|
||||
grid-template-columns: 6fr 4fr;
|
||||
padding-inline: 2.5rem;
|
||||
gap: 3.75rem;
|
||||
}
|
||||
|
||||
.roles {
|
||||
margin-top: 0.5rem;
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.hero img {
|
||||
aspect-ratio: 3 / 4;
|
||||
border-radius: 4.5rem;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
/* ====================================================== */
|
||||
|
||||
.section {
|
||||
display: grid;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.with-background {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.with-background::before {
|
||||
--hero-bg: var(--bg-image-subtle-2);
|
||||
|
||||
content: '';
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
left: 50%;
|
||||
width: 100vw;
|
||||
aspect-ratio: calc(2.25 / var(--bg-scale));
|
||||
top: 0;
|
||||
transform: translateY(-75%) translateX(-50%);
|
||||
background:
|
||||
url('/assets/backgrounds/noise.png') top center/220px repeat,
|
||||
var(--hero-bg) center center / var(--bg-gradient-size) no-repeat,
|
||||
var(--gray-999);
|
||||
background-blend-mode: overlay, normal, normal, normal;
|
||||
mix-blend-mode: var(--bg-blend-mode);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.with-background.bg-variant::before {
|
||||
--hero-bg: var(--bg-image-subtle-1);
|
||||
}
|
||||
|
||||
.section-header {
|
||||
justify-self: center;
|
||||
text-align: center;
|
||||
max-width: 50ch;
|
||||
font-size: var(--text-md);
|
||||
color: var(--gray-300);
|
||||
}
|
||||
|
||||
.section-header h3 {
|
||||
font-size: var(--text-2xl);
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
.section {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
grid-template-areas: 'header header header header' 'gallery gallery gallery gallery';
|
||||
gap: 5rem;
|
||||
}
|
||||
|
||||
.section.with-cta {
|
||||
grid-template-areas: 'header header header cta' 'gallery gallery gallery gallery';
|
||||
}
|
||||
|
||||
.section-header {
|
||||
grid-area: header;
|
||||
font-size: var(--text-lg);
|
||||
}
|
||||
|
||||
.section-header h3 {
|
||||
font-size: var(--text-4xl);
|
||||
}
|
||||
|
||||
.with-cta .section-header {
|
||||
justify-self: flex-start;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.gallery {
|
||||
grid-area: gallery;
|
||||
}
|
||||
|
||||
.cta {
|
||||
grid-area: cta;
|
||||
}
|
||||
}
|
||||
|
||||
/* ====================================================== */
|
||||
|
||||
.mention-card {
|
||||
display: flex;
|
||||
height: 7rem;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
border: 1px solid var(--gray-800);
|
||||
border-radius: 1.5rem;
|
||||
color: var(--gray-300);
|
||||
background: var(--gradient-subtle);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
.mention-card {
|
||||
border-radius: 1.5rem;
|
||||
height: 9.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,23 +0,0 @@
|
|||
import rss from "@astrojs/rss";
|
||||
import { getCollection } from "astro:content";
|
||||
import sanitizeHtml from 'sanitize-html';
|
||||
import MarkdownIt from 'markdown-it';
|
||||
const parser = new MarkdownIt();
|
||||
|
||||
export async function GET(context) {
|
||||
const blog = await getCollection("blog");
|
||||
return rss({
|
||||
title: "Toastie_t0ast’s Blog",
|
||||
description: "my blog",
|
||||
site: context.site,
|
||||
items: blog.map((post) => ({
|
||||
title: post.data.title,
|
||||
pubDate: post.data.pubDate,
|
||||
description: post.data.description,
|
||||
content: sanitizeHtml(parser.render(post.body)),
|
||||
// Compute RSS link from post `slug`
|
||||
// This example assumes all posts are rendered as `/blog/[slug]` routes
|
||||
link: `/blog/${post.slug}/`,
|
||||
})),
|
||||
});
|
||||
}
|
39
src/pages/work.astro
Normal file
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
import { getCollection } from 'astro:content';
|
||||
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
|
||||
import ContactCTA from '../components/ContactCTA.astro';
|
||||
import PortfolioPreview from '../components/PortfolioPreview.astro';
|
||||
import Hero from '../components/Hero.astro';
|
||||
import Grid from '../components/Grid.astro';
|
||||
|
||||
const projects = (await getCollection('work')).sort(
|
||||
(a, b) => b.data.publishDate.valueOf() - a.data.publishDate.valueOf(),
|
||||
);
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
title="My Work | Toastie_t0ast"
|
||||
description="Learn about Toastie_t0ast's most recent projects"
|
||||
>
|
||||
<div class="stack gap-20">
|
||||
<main class="wrapper stack gap-8">
|
||||
<Hero
|
||||
title="My Work"
|
||||
tagline="See my most recent projects below to get an idea of my past experience."
|
||||
align="start"
|
||||
/>
|
||||
<Grid variant="offset">
|
||||
{
|
||||
projects.map((project) => (
|
||||
<li>
|
||||
<PortfolioPreview project={project} />
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</Grid>
|
||||
</main>
|
||||
<ContactCTA />
|
||||
</div>
|
||||
</BaseLayout>
|
152
src/pages/work/[...slug].astro
Normal file
|
@ -0,0 +1,152 @@
|
|||
---
|
||||
import { type CollectionEntry, getCollection } from 'astro:content';
|
||||
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
|
||||
import ContactCTA from '../../components/ContactCTA.astro';
|
||||
import Hero from '../../components/Hero.astro';
|
||||
import Icon from '../../components/Icon.astro';
|
||||
import Pill from '../../components/Pill.astro';
|
||||
import { render } from 'astro:content';
|
||||
|
||||
interface Props {
|
||||
entry: CollectionEntry<'work'>;
|
||||
}
|
||||
|
||||
// This is a dynamic route that generates a page for every Markdown file in src/content/
|
||||
// Read more about dynamic routes and this `getStaticPaths` function in the Astro docs:
|
||||
// https://docs.astro.build/en/core-concepts/routing/#dynamic-routes
|
||||
export async function getStaticPaths() {
|
||||
const work = await getCollection('work');
|
||||
return work.map((entry) => ({
|
||||
params: { slug: entry.id },
|
||||
props: { entry },
|
||||
}));
|
||||
}
|
||||
|
||||
const { entry } = Astro.props;
|
||||
const { Content } = await render(entry);
|
||||
---
|
||||
|
||||
<BaseLayout title={entry.data.title} description={entry.data.description}>
|
||||
<div class="stack gap-20">
|
||||
<div class="stack gap-15">
|
||||
<header>
|
||||
<div class="wrapper stack gap-2">
|
||||
<a class="back-link" href="/work/"><Icon icon="arrow-left" /> Work</a>
|
||||
<Hero title={entry.data.title} align="start">
|
||||
<div class="details">
|
||||
<div class="tags">
|
||||
{entry.data.tags.map((t) => <Pill>{t}</Pill>)}
|
||||
</div>
|
||||
<p class="description">{entry.data.description}</p>
|
||||
</div>
|
||||
</Hero>
|
||||
</div>
|
||||
</header>
|
||||
<main class="wrapper">
|
||||
<div class="stack gap-10 content">
|
||||
{entry.data.img && <img src={entry.data.img} alt={entry.data.img_alt || ''} />}
|
||||
<div class="content">
|
||||
<Content />
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<ContactCTA />
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
||||
<style>
|
||||
header {
|
||||
padding-bottom: 2.5rem;
|
||||
border-bottom: 1px solid var(--gray-800);
|
||||
}
|
||||
|
||||
.back-link {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0.5rem;
|
||||
gap: 1.5rem;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tags {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: var(--text-lg);
|
||||
max-width: 54ch;
|
||||
}
|
||||
|
||||
.content {
|
||||
max-width: 65ch;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.content > :global(* + *) {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.content :global(h1),
|
||||
.content :global(h2),
|
||||
.content :global(h3),
|
||||
.content :global(h4),
|
||||
.content :global(h5) {
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.content :global(img) {
|
||||
border-radius: 1.5rem;
|
||||
box-shadow: var(--shadow-sm);
|
||||
background: var(--gradient-subtle);
|
||||
border: 1px solid var(--gray-800);
|
||||
}
|
||||
|
||||
.content :global(blockquote) {
|
||||
font-size: var(--text-lg);
|
||||
font-family: var(--font-brand);
|
||||
font-weight: 600;
|
||||
line-height: 1.1;
|
||||
padding-inline-start: 1.5rem;
|
||||
border-inline-start: 0.25rem solid var(--accent-dark);
|
||||
color: var(--gray-0);
|
||||
}
|
||||
|
||||
.back-link,
|
||||
.content :global(a) {
|
||||
text-decoration: 1px solid underline transparent;
|
||||
text-underline-offset: 0.25em;
|
||||
transition: text-decoration-color var(--theme-transition);
|
||||
}
|
||||
|
||||
.back-link:hover,
|
||||
.back-link:focus,
|
||||
.content :global(a:hover),
|
||||
.content :global(a:focus) {
|
||||
text-decoration-color: currentColor;
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
.back-link {
|
||||
display: block;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.details {
|
||||
flex-direction: row;
|
||||
gap: 2.5rem;
|
||||
}
|
||||
|
||||
.content :global(blockquote) {
|
||||
font-size: var(--text-2xl);
|
||||
}
|
||||
}
|
||||
</style>
|
255
src/styles/global.css
Normal file
|
@ -0,0 +1,255 @@
|
|||
/* Global variables */
|
||||
:root {
|
||||
/* Colors */
|
||||
--gray-0: #090b11;
|
||||
--gray-50: #141925;
|
||||
--gray-100: #283044;
|
||||
--gray-200: #3d4663;
|
||||
--gray-300: #505d84;
|
||||
--gray-400: #6474a2;
|
||||
--gray-500: #8490b5;
|
||||
--gray-600: #a3acc8;
|
||||
--gray-700: #c3cadb;
|
||||
--gray-800: #e3e6ee;
|
||||
--gray-900: #f3f4f7;
|
||||
--gray-999-basis: 0, 0%, 100%;
|
||||
--gray-999_40: hsla(var(--gray-999-basis), 0.4);
|
||||
--gray-999: #ffffff;
|
||||
|
||||
--accent-light: #c561f6;
|
||||
--accent-regular: #7611a6;
|
||||
--accent-dark: #1c0056;
|
||||
--accent-overlay: hsla(280, 89%, 67%, 0.33);
|
||||
--accent-subtle-overlay: var(--accent-overlay);
|
||||
--accent-text-over: var(--gray-999);
|
||||
|
||||
--link-color: var(--accent-regular);
|
||||
|
||||
/* Gradients */
|
||||
--gradient-stop-1: var(--accent-light);
|
||||
--gradient-stop-2: var(--accent-regular);
|
||||
--gradient-stop-3: var(--accent-dark);
|
||||
--gradient-subtle: linear-gradient(150deg, var(--gray-900) 19%, var(--gray-999) 150%);
|
||||
--gradient-accent: linear-gradient(
|
||||
150deg,
|
||||
var(--gradient-stop-1),
|
||||
var(--gradient-stop-2),
|
||||
var(--gradient-stop-3)
|
||||
);
|
||||
--gradient-accent-orange: linear-gradient(
|
||||
150deg,
|
||||
#ca7879,
|
||||
var(--accent-regular),
|
||||
var(--accent-dark)
|
||||
);
|
||||
--gradient-stroke: linear-gradient(180deg, var(--gray-900), var(--gray-700));
|
||||
|
||||
/* Shadows */
|
||||
--shadow-sm: 0px 6px 3px rgba(9, 11, 17, 0.01), 0px 4px 2px rgba(9, 11, 17, 0.01),
|
||||
0px 2px 2px rgba(9, 11, 17, 0.02), 0px 0px 1px rgba(9, 11, 17, 0.03);
|
||||
--shadow-md: 0px 28px 11px rgba(9, 11, 17, 0.01), 0px 16px 10px rgba(9, 11, 17, 0.03),
|
||||
0px 7px 7px rgba(9, 11, 17, 0.05), 0px 2px 4px rgba(9, 11, 17, 0.06);
|
||||
--shadow-lg: 0px 62px 25px rgba(9, 11, 17, 0.01), 0px 35px 21px rgba(9, 11, 17, 0.05),
|
||||
0px 16px 16px rgba(9, 11, 17, 0.1), 0px 4px 9px rgba(9, 11, 17, 0.12);
|
||||
|
||||
/* Text Sizes */
|
||||
--text-sm: 0.875rem;
|
||||
--text-base: 1rem;
|
||||
--text-md: 1.125rem;
|
||||
--text-lg: 1.25rem;
|
||||
--text-xl: 1.625rem;
|
||||
--text-2xl: 2.125rem;
|
||||
--text-3xl: 2.625rem;
|
||||
--text-4xl: 3.5rem;
|
||||
--text-5xl: 4.5rem;
|
||||
|
||||
/* Fonts */
|
||||
--font-system: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
|
||||
Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
--font-body: 'Public Sans', var(--font-system);
|
||||
--font-brand: Rubik, var(--font-system);
|
||||
|
||||
/* Transitions */
|
||||
--theme-transition: 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
:root.theme-dark {
|
||||
--gray-0: #ffffff;
|
||||
--gray-50: #f3f4f7;
|
||||
--gray-100: #e3e6ee;
|
||||
--gray-200: #c3cadb;
|
||||
--gray-300: #a3acc8;
|
||||
--gray-400: #8490b5;
|
||||
--gray-500: #6474a2;
|
||||
--gray-600: #505d84;
|
||||
--gray-700: #3d4663;
|
||||
--gray-800: #283044;
|
||||
--gray-900: #141925;
|
||||
--gray-999-basis: 225, 31%, 5%;
|
||||
--gray-999: #090b11;
|
||||
|
||||
--accent-light: #1c0056;
|
||||
--accent-regular: #7611a6;
|
||||
--accent-dark: #c561f6;
|
||||
--accent-overlay: hsla(280, 89%, 67%, 0.33);
|
||||
--accent-subtle-overlay: hsla(281, 81%, 36%, 0.33);
|
||||
--accent-text-over: var(--gray-0);
|
||||
|
||||
--link-color: var(--accent-dark);
|
||||
|
||||
--gradient-stop-1: #4c11c6;
|
||||
--gradient-subtle: linear-gradient(150deg, var(--gray-900) 19%, var(--gray-999) 81%);
|
||||
--gradient-accent-orange: linear-gradient(
|
||||
150deg,
|
||||
#ca7879,
|
||||
var(--accent-regular),
|
||||
var(--accent-light)
|
||||
);
|
||||
--gradient-stroke: linear-gradient(180deg, var(--gray-600), var(--gray-800));
|
||||
|
||||
--shadow-sm: 0px 6px 3px rgba(255, 255, 255, 0.01), 0px 4px 2px rgba(255, 255, 255, 0.01),
|
||||
0px 2px 2px rgba(255, 255, 255, 0.02), 0px 0px 1px rgba(255, 255, 255, 0.03);
|
||||
--shadow-md: 0px 28px 11px rgba(255, 255, 255, 0.01), 0px 16px 10px rgba(255, 255, 255, 0.03),
|
||||
0px 7px 7px rgba(255, 255, 255, 0.05), 0px 2px 4px rgba(255, 255, 255, 0.06);
|
||||
--shadow-lg: 0px 62px 25px rgba(255, 255, 255, 0.01), 0px 35px 21px rgba(255, 255, 255, 0.05),
|
||||
0px 16px 16px rgba(255, 255, 255, 0.1), 0px 4px 9px rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
min-height: 100%;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--gray-999);
|
||||
color: var(--gray-200);
|
||||
font-family: var(--font-body);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
*,
|
||||
*::after,
|
||||
*::before {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--link-color);
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5 {
|
||||
line-height: 1.1;
|
||||
font-family: var(--font-brand);
|
||||
font-weight: 600;
|
||||
color: var(--gray-100);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: var(--text-5xl);
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: var(--text-4xl);
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: var(--text-3xl);
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: var(--text-2xl);
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: var(--text-xl);
|
||||
}
|
||||
|
||||
/* Utilities */
|
||||
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
width: 100%;
|
||||
max-width: 83rem;
|
||||
margin-inline: auto;
|
||||
padding-inline: 1.5rem;
|
||||
}
|
||||
|
||||
.stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.gap-2 {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.gap-4 {
|
||||
gap: 1rem;
|
||||
}
|
||||
.gap-8 {
|
||||
gap: 2rem;
|
||||
}
|
||||
.gap-10 {
|
||||
gap: 2.5rem;
|
||||
}
|
||||
.gap-15 {
|
||||
gap: 3.75rem;
|
||||
}
|
||||
.gap-20 {
|
||||
gap: 5rem;
|
||||
}
|
||||
.gap-30 {
|
||||
gap: 7.5rem;
|
||||
}
|
||||
.gap-48 {
|
||||
gap: 12rem;
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
.lg\:gap-2 {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.lg\:gap-4 {
|
||||
gap: 1rem;
|
||||
}
|
||||
.lg\:gap-8 {
|
||||
gap: 2rem;
|
||||
}
|
||||
.lg\:gap-10 {
|
||||
gap: 2.5rem;
|
||||
}
|
||||
.lg\:gap-15 {
|
||||
gap: 3.75rem;
|
||||
}
|
||||
.lg\:gap-20 {
|
||||
gap: 5rem;
|
||||
}
|
||||
.lg\:gap-30 {
|
||||
gap: 7.5rem;
|
||||
}
|
||||
.lg\:gap-48 {
|
||||
gap: 12rem;
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
import { vitePreprocess } from '@astrojs/svelte';
|
||||
|
||||
export default {
|
||||
preprocess: vitePreprocess(),
|
||||
};
|
|
@ -1,7 +1,5 @@
|
|||
{
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"compilerOptions": {
|
||||
"jsx": "preserve",
|
||||
"jsxImportSource": "solid-js"
|
||||
}
|
||||
"include": [".astro/types.d.ts", "**/*"],
|
||||
"exclude": ["dist"]
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
// uno.config.ts
|
||||
import { defineConfig, presetUno, presetWebFonts } from "unocss";
|
||||
|
||||
export default defineConfig({
|
||||
content: {
|
||||
filesystem: ["**/*.{html,js,ts,jsx,tsx,vue,svelte,astro}"],
|
||||
},
|
||||
theme: {
|
||||
boxShadow: {
|
||||
custom: `2px 2px 0`,
|
||||
"custom-hover": `1px 1px 0`,
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ["CabinetGrotesk", "Satoshi"],
|
||||
},
|
||||
gridTemplateRows: {
|
||||
"auto-250": "repeat(auto-fill, 250px)",
|
||||
},
|
||||
gridTemplateColumns: {
|
||||
"4-minmax": "repeat(4, minmax(150px, 1fr))",
|
||||
},
|
||||
colors: {
|
||||
gray: {
|
||||
50: "#FAFAFA",
|
||||
100: "#F5F5F5",
|
||||
200: "#E5E5E5",
|
||||
300: "#D4D4D4",
|
||||
400: "#A3A3A3",
|
||||
500: "#737373",
|
||||
600: "#525252",
|
||||
700: "#404040",
|
||||
800: "#262626",
|
||||
900: "#171717",
|
||||
},
|
||||
darkslate: {
|
||||
50: "#3D3D3D",
|
||||
100: "#2C2C2C",
|
||||
200: "#262626",
|
||||
300: "#202020",
|
||||
400: "#1A1A1A",
|
||||
500: "#171717" /* Exactly your example for the background */,
|
||||
600: "#141414",
|
||||
700: "#111111",
|
||||
800: "#0E0E0E",
|
||||
900: "#0B0B0B" /* Deeper and darker */,
|
||||
},
|
||||
primary: {
|
||||
100: "#F9CDD3",
|
||||
200: "#F3A3AA",
|
||||
300: "#EC7981",
|
||||
400: "#E64F59",
|
||||
500: "#E63946",
|
||||
600: "#CF2F3D",
|
||||
700: "#B82534",
|
||||
800: "#A01B2B",
|
||||
900: "#891321",
|
||||
},
|
||||
},
|
||||
},
|
||||
presets: [
|
||||
presetUno(),
|
||||
presetWebFonts({
|
||||
provider: "fontshare",
|
||||
fonts: {
|
||||
sans: ["Cabinet Grotesk", "Satoshi"],
|
||||
serif: "Zodiak",
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|