Initial commit
This commit is contained in:
parent
bb58f603b2
commit
83f018c3b8
1
.env.development
Normal file
1
.env.development
Normal file
@ -0,0 +1 @@
|
||||
URL="http://localhost:4321"
|
1
.env.production
Normal file
1
.env.production
Normal file
@ -0,0 +1 @@
|
||||
URL="https://martials.no"
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -14,8 +14,8 @@ yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# environment variables
|
||||
.env
|
||||
.env.production
|
||||
.env.local
|
||||
.env.production.local
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
@ -1,5 +1,21 @@
|
||||
// @ts-check
|
||||
import { defineConfig } from 'astro/config';
|
||||
import { defineConfig } from "astro/config"
|
||||
import tailwind from "@astrojs/tailwind"
|
||||
import sitemap from "@astrojs/sitemap"
|
||||
import { loadEnv } from "vite"
|
||||
import mdx from "@astrojs/mdx"
|
||||
import svelte from "@astrojs/svelte"
|
||||
|
||||
const { url } = process.env.URL
|
||||
? loadEnv(process.env.URL, process.cwd(), "")
|
||||
: { url: "http://localhost:3000" }
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({});
|
||||
export default defineConfig({
|
||||
site: url,
|
||||
i18n: {
|
||||
defaultLocale: "nb",
|
||||
locales: ["nb", "en"]
|
||||
},
|
||||
integrations: [tailwind(), sitemap(), mdx(), svelte()]
|
||||
})
|
||||
|
14
messages/en.json
Normal file
14
messages/en.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"$schema": "https://inlang.com/schema/inlang-message-format",
|
||||
"hiIm": "Hi, I'm",
|
||||
"position": "Software Engineer",
|
||||
"aboutMe": "Some bullshit about me",
|
||||
"home": "Home",
|
||||
"contactMe": "Contact me",
|
||||
"myLinks": "My links",
|
||||
"myProjects": "My projects",
|
||||
"hardware": "Hardware",
|
||||
"sourceCode": "Source code",
|
||||
"createdAt": "Created at",
|
||||
"updatedAt": "Updated at"
|
||||
}
|
14
messages/nb.json
Normal file
14
messages/nb.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"$schema": "https://inlang.com/schema/inlang-message-format",
|
||||
"hiIm": "Hei, jeg er",
|
||||
"position": "Programvareutvikler",
|
||||
"aboutMe": "Noe tull om meg",
|
||||
"home": "Hjem",
|
||||
"contactMe": "Kontakt meg",
|
||||
"myLinks": "Mine lenker",
|
||||
"myProjects": "Mine prosjekter",
|
||||
"hardware": "Maskinvare",
|
||||
"sourceCode": "Kildekode",
|
||||
"createdAt": "Opprettet",
|
||||
"updatedAt": "Oppdatert"
|
||||
}
|
6392
package-lock.json
generated
6392
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
47
package.json
47
package.json
@ -5,13 +5,52 @@
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro check && astro build",
|
||||
"build": "paraglide-js compile --project ./project.inlang --outdir ./src/paraglide && astro check && astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro"
|
||||
"astro": "astro",
|
||||
"postinstall": "paraglide-js compile --project ./project.inlang --outdir ./src/paraglide",
|
||||
"format": "prettier --write \"./src/**/*.{js,mjs,ts,astro,svelte,css,md,json}\""
|
||||
},
|
||||
"dependencies": {
|
||||
"astro": "^4.15.4",
|
||||
"@astrojs/check": "^0.9.3",
|
||||
"@astrojs/mdx": "^3.1.5",
|
||||
"@astrojs/sitemap": "^3.1.6",
|
||||
"@astrojs/svelte": "^5.7.0",
|
||||
"@astrojs/tailwind": "^5.1.0",
|
||||
"@tailwindcss/typography": "^0.5.15",
|
||||
"astro": "^4.15.4",
|
||||
"svelte": "^4.2.19",
|
||||
"tailwindcss": "^3.4.10",
|
||||
"typescript": "^5.5.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@inlang/paraglide-js": "1.11.2",
|
||||
"daisyui": "^4.12.10",
|
||||
"prettier": "^3.3.3",
|
||||
"prettier-plugin-astro": "^0.14.1",
|
||||
"prettier-plugin-svelte": "^3.2.6",
|
||||
"vite": "^5.4.3"
|
||||
},
|
||||
"prettier": {
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"plugins": [
|
||||
"prettier-plugin-astro",
|
||||
"prettier-plugin-svelte"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": "**/*.astro",
|
||||
"options": {
|
||||
"parser": "astro"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": "*.svelte",
|
||||
"options": {
|
||||
"parser": "svelte"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
5438
pnpm-lock.yaml
generated
Normal file
5438
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
1
project.inlang/.gitignore
vendored
Normal file
1
project.inlang/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
cache
|
1
project.inlang/project_id
Normal file
1
project.inlang/project_id
Normal file
@ -0,0 +1 @@
|
||||
77f19dd4021ac3c23f6938b6214af958782a6bf5e376771917a1f4134873961e
|
15
project.inlang/settings.json
Normal file
15
project.inlang/settings.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://inlang.com/schema/project-settings",
|
||||
"sourceLanguageTag": "nb",
|
||||
"languageTags": ["nb", "en"],
|
||||
"modules": [
|
||||
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-empty-pattern@latest/dist/index.js",
|
||||
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-missing-translation@latest/dist/index.js",
|
||||
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-without-source@latest/dist/index.js",
|
||||
"https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@latest/dist/index.js",
|
||||
"https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@latest/dist/index.js"
|
||||
],
|
||||
"plugin.inlang.messageFormat": {
|
||||
"pathPattern": "./messages/{languageTag}.json"
|
||||
}
|
||||
}
|
@ -1,61 +1,61 @@
|
||||
---
|
||||
interface Props {
|
||||
title: string;
|
||||
body: string;
|
||||
href: string;
|
||||
title: string
|
||||
body: string
|
||||
href: string
|
||||
}
|
||||
|
||||
const { href, title, body } = Astro.props;
|
||||
const { href, title, body } = Astro.props
|
||||
---
|
||||
|
||||
<li class="link-card">
|
||||
<a href={href}>
|
||||
<h2>
|
||||
{title}
|
||||
<span>→</span>
|
||||
</h2>
|
||||
<p>
|
||||
{body}
|
||||
</p>
|
||||
</a>
|
||||
<a href={href}>
|
||||
<h2>
|
||||
{title}
|
||||
<span>→</span>
|
||||
</h2>
|
||||
<p>
|
||||
{body}
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
<style>
|
||||
.link-card {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
padding: 1px;
|
||||
background-color: #23262d;
|
||||
background-image: none;
|
||||
background-size: 400%;
|
||||
border-radius: 7px;
|
||||
background-position: 100%;
|
||||
transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1);
|
||||
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.link-card > a {
|
||||
width: 100%;
|
||||
text-decoration: none;
|
||||
line-height: 1.4;
|
||||
padding: calc(1.5rem - 1px);
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
background-color: #23262d;
|
||||
opacity: 0.8;
|
||||
}
|
||||
h2 {
|
||||
margin: 0;
|
||||
font-size: 1.25rem;
|
||||
transition: color 0.6s cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
p {
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.link-card:is(:hover, :focus-within) {
|
||||
background-position: 0;
|
||||
background-image: var(--accent-gradient);
|
||||
}
|
||||
.link-card:is(:hover, :focus-within) h2 {
|
||||
color: rgb(var(--accent-light));
|
||||
}
|
||||
.link-card {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
padding: 1px;
|
||||
background-color: #23262d;
|
||||
background-image: none;
|
||||
background-size: 400%;
|
||||
border-radius: 7px;
|
||||
background-position: 100%;
|
||||
transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1);
|
||||
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.link-card > a {
|
||||
width: 100%;
|
||||
text-decoration: none;
|
||||
line-height: 1.4;
|
||||
padding: calc(1.5rem - 1px);
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
background-color: #23262d;
|
||||
opacity: 0.8;
|
||||
}
|
||||
h2 {
|
||||
margin: 0;
|
||||
font-size: 1.25rem;
|
||||
transition: color 0.6s cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
p {
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.link-card:is(:hover, :focus-within) {
|
||||
background-position: 0;
|
||||
background-image: var(--accent-gradient);
|
||||
}
|
||||
.link-card:is(:hover, :focus-within) h2 {
|
||||
color: rgb(var(--accent-light));
|
||||
}
|
||||
</style>
|
||||
|
37
src/components/ContactMeForm.astro
Normal file
37
src/components/ContactMeForm.astro
Normal file
@ -0,0 +1,37 @@
|
||||
---
|
||||
// TODO form
|
||||
// TODO self-host email server
|
||||
import "../styles/global.css"
|
||||
import Input from "../components/Input.astro"
|
||||
import * as console from "node:console"
|
||||
|
||||
if (Astro.request.method === "POST") {
|
||||
try {
|
||||
const data = await Astro.request.formData()
|
||||
const name = data.get("name")
|
||||
const subject = data.get("subject")
|
||||
const email = data.get("email")
|
||||
const message = data.get("message")
|
||||
// TODO Do something with the data
|
||||
console.info({ name, subject, email, message })
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
---
|
||||
|
||||
<div class="text-red-600 text-center">In development</div>
|
||||
|
||||
<form class="flex flex-col gap-2 max-w-[500px] mx-auto" method="post">
|
||||
<Input label="Name" type="text" name="name" required />
|
||||
<Input label="Subject" name="subject" required />
|
||||
<Input label="Email" name="email" />
|
||||
<label class="flex flex-col"
|
||||
>Message
|
||||
<textarea name="message" class="textarea textarea-bordered" required
|
||||
></textarea>
|
||||
</label>
|
||||
<button type="submit">Send</button>
|
||||
</form>
|
9
src/components/Footer.astro
Normal file
9
src/components/Footer.astro
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
import GiteaLink from "./links/GiteaLink.astro"
|
||||
const gitUrl = import.meta.env.GIT_URL
|
||||
---
|
||||
|
||||
<div class="divider"></div>
|
||||
<div class="mx-auto">
|
||||
<GiteaLink href={gitUrl} />
|
||||
</div>
|
20
src/components/Greeting.astro
Normal file
20
src/components/Greeting.astro
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
import { Image } from "astro:assets"
|
||||
import me from "../images/me.jpg"
|
||||
import * as m from "../paraglide/messages.js"
|
||||
import "../styles/global.css"
|
||||
---
|
||||
|
||||
<div class="flex items-center justify-around">
|
||||
<div class="m-5">
|
||||
<h1 class="text-7xl font-bold">
|
||||
{m.hiIm()}
|
||||
<br />
|
||||
Martin Berg Alstad
|
||||
<br />
|
||||
{m.position()}
|
||||
</h1>
|
||||
<p class="mx-1 my-10">{m.aboutMe()}</p>
|
||||
</div>
|
||||
<Image src={me} alt="Me on a hike" width="400" height="400" class="m-5" />
|
||||
</div>
|
11
src/components/HardwarePage.svelte
Normal file
11
src/components/HardwarePage.svelte
Normal file
@ -0,0 +1,11 @@
|
||||
<script lang="ts">
|
||||
import Select from "./Select.svelte"
|
||||
|
||||
function onChange({ detail }: CustomEvent<string>) {
|
||||
console.log(detail)
|
||||
}
|
||||
|
||||
// TODO show the selected hardware
|
||||
</script>
|
||||
|
||||
<Select options={["CPU", "GPU", "RAM"]} on:change={onChange} />
|
28
src/components/Input.astro
Normal file
28
src/components/Input.astro
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
interface Props {
|
||||
label: string
|
||||
type?: "text" | "email" | "password" | "number"
|
||||
name: string
|
||||
required?: boolean
|
||||
placeholder?: string
|
||||
}
|
||||
|
||||
const {
|
||||
label,
|
||||
type = "text",
|
||||
name,
|
||||
required = false,
|
||||
placeholder,
|
||||
} = Astro.props
|
||||
---
|
||||
|
||||
<label class="flex flex-col">
|
||||
{label}
|
||||
<input
|
||||
class="input input-bordered"
|
||||
type={type}
|
||||
name={name}
|
||||
required={required}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
</label>
|
13
src/components/Navbar.astro
Normal file
13
src/components/Navbar.astro
Normal file
@ -0,0 +1,13 @@
|
||||
---
|
||||
import Links from "../links"
|
||||
---
|
||||
|
||||
<div class="flex justify-end">
|
||||
{
|
||||
Links.map(({ to, label }) => (
|
||||
<a href={to} class="m-2 hover:underline">
|
||||
{label}
|
||||
</a>
|
||||
))
|
||||
}
|
||||
</div>
|
32
src/components/ProjectCard.astro
Normal file
32
src/components/ProjectCard.astro
Normal file
@ -0,0 +1,32 @@
|
||||
---
|
||||
import { Image } from "astro:assets"
|
||||
import { type ImageMetadata } from "astro"
|
||||
import BadgeList from "./badge/BadgeList.astro"
|
||||
|
||||
interface Props {
|
||||
title: string
|
||||
description: string
|
||||
tags: string[]
|
||||
image: ImageMetadata
|
||||
imageAlt: string
|
||||
linkTo: string
|
||||
}
|
||||
|
||||
const { title, description, tags, image, imageAlt, linkTo } = Astro.props
|
||||
---
|
||||
|
||||
<a
|
||||
href={linkTo}
|
||||
class="card bg-base-100 w-96 shadow-xl hover:scale-105 transition"
|
||||
>
|
||||
<figure>
|
||||
<Image src={image} alt={imageAlt} />
|
||||
</figure>
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">
|
||||
{title}
|
||||
</h2>
|
||||
<p>{description}</p>
|
||||
<BadgeList tags={tags} />
|
||||
</div>
|
||||
</a>
|
55
src/components/ProjectPage.astro
Normal file
55
src/components/ProjectPage.astro
Normal file
@ -0,0 +1,55 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro"
|
||||
import { Image } from "astro:assets"
|
||||
import { getEntry } from "astro:content"
|
||||
import BadgeList from "./badge/BadgeList.astro"
|
||||
import ExternalLink from "./links/ExternalLink.astro"
|
||||
import * as m from "../paraglide/messages"
|
||||
import { languageTag } from "../paraglide/runtime"
|
||||
import Gitea from "../icons/Gitea.astro"
|
||||
import "../styles/global.css"
|
||||
import GiteaLink from "./links/GiteaLink.astro"
|
||||
|
||||
interface Props {
|
||||
project: string // TODO typeof project slug
|
||||
}
|
||||
|
||||
const { project } = Astro.props
|
||||
|
||||
const entry = await getEntry("projects", project)
|
||||
const { Content } = await entry!.render()
|
||||
const {
|
||||
title,
|
||||
description,
|
||||
tags,
|
||||
heroImage,
|
||||
heroImageAlt,
|
||||
source,
|
||||
createdAt,
|
||||
updatedAt
|
||||
} = entry!.data
|
||||
---
|
||||
|
||||
<!--TODO day.js for dates?-->
|
||||
<Layout title={title} class="mx-auto max-w-[750px]">
|
||||
<div class="flex justify-between my-2">
|
||||
<div>
|
||||
<h1>{title}</h1>
|
||||
<BadgeList tags={tags} />
|
||||
</div>
|
||||
<div class="flex flex-col items-end">
|
||||
<p>
|
||||
{m.createdAt()}: {new Date(createdAt).toLocaleDateString(languageTag())}
|
||||
</p>
|
||||
<p>
|
||||
{m.updatedAt()}: {new Date(updatedAt).toLocaleDateString(languageTag())}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Image src={heroImage} alt={heroImageAlt} />
|
||||
|
||||
<GiteaLink href={source} />
|
||||
|
||||
<p class="my-2">{description}</p>
|
||||
<Content />
|
||||
</Layout>
|
13
src/components/Select.svelte
Normal file
13
src/components/Select.svelte
Normal file
@ -0,0 +1,13 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte"
|
||||
|
||||
export let options: string[] = []
|
||||
const dispatch = createEventDispatcher<{ change: string }>()
|
||||
</script>
|
||||
|
||||
<select class="select select-bordered w-full max-w-xs"
|
||||
on:change={(value) => dispatch("change", value.currentTarget.value)}>
|
||||
{#each options as option}
|
||||
<option value={option}>{option}</option>
|
||||
{/each}
|
||||
</select>
|
97
src/components/Terminal.svelte
Normal file
97
src/components/Terminal.svelte
Normal file
@ -0,0 +1,97 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte"
|
||||
|
||||
let history: string[] = []
|
||||
let currentDir = "~"
|
||||
|
||||
type Command = "help" | "about" | "skills" | "projects" | "contact" | "clear"
|
||||
|
||||
const commands: Record<Command, () => string> = {
|
||||
help: () => `Available commands:
|
||||
about - Display information about me
|
||||
skills - List my technical skills
|
||||
projects - Show my notable projects
|
||||
contact - Display my contact information
|
||||
clear - Clear the terminal screen`,
|
||||
about: () => `Hi, I'm John Doe!
|
||||
I'm a passionate software developer with 5 years of experience.
|
||||
I love creating elegant solutions to complex problems.`,
|
||||
skills: () => `My technical skills include:
|
||||
- JavaScript/TypeScript
|
||||
- React & Next.js
|
||||
- Node.js
|
||||
- Python
|
||||
- SQL & NoSQL databases`,
|
||||
projects: () => `Some of my notable projects:
|
||||
1. E-commerce Platform (React, Node.js, MongoDB)
|
||||
2. Weather App (React Native, OpenWeatherMap API)
|
||||
3. Task Management System (Python, Django, PostgreSQL)`,
|
||||
contact: () => `You can reach me at:
|
||||
Email: john.doe@example.com
|
||||
GitHub: github.com/johndoe
|
||||
LinkedIn: linkedin.com/in/johndoe`,
|
||||
clear: () => {
|
||||
history = []
|
||||
return ""
|
||||
},
|
||||
}
|
||||
|
||||
const executeCommand = (input: string) => {
|
||||
const [command, ...args] = input.trim().split(" ")
|
||||
if (command in commands) {
|
||||
return commands[command as Command]()
|
||||
}
|
||||
return `Command not found: ${command}. Type 'help' for available commands.`
|
||||
}
|
||||
|
||||
let input = ""
|
||||
let inputRef: HTMLInputElement | null = null
|
||||
|
||||
const handleSubmit = (e: Event) => {
|
||||
e.preventDefault()
|
||||
if (input.trim()) {
|
||||
if (input === "clear") {
|
||||
history = []
|
||||
} else {
|
||||
history = [
|
||||
...history,
|
||||
`${currentDir} $ ${input}`,
|
||||
executeCommand(input),
|
||||
]
|
||||
}
|
||||
input = ""
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
history = [
|
||||
"Welcome to John Doe's Terminal Portfolio!",
|
||||
"Type 'help' to see available commands.",
|
||||
]
|
||||
})
|
||||
|
||||
$: {
|
||||
if (inputRef) {
|
||||
inputRef.scrollIntoView({ behavior: "smooth" })
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="min-h-screen bg-black text-green-500 p-4 font-mono">
|
||||
<div class="max-w-3xl mx-auto">
|
||||
<div class="mb-4">
|
||||
{#each history as line}
|
||||
<pre class="whitespace-pre-wrap">{line}</pre>
|
||||
{/each}
|
||||
</div>
|
||||
<form on:submit={handleSubmit} class="flex">
|
||||
<span class="mr-2">{currentDir} $</span>
|
||||
<input
|
||||
bind:this={inputRef}
|
||||
bind:value={input}
|
||||
type="text"
|
||||
class="flex-grow bg-transparent outline-none"
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
8
src/components/badge/Badge.astro
Normal file
8
src/components/badge/Badge.astro
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
interface Props {
|
||||
tag: string
|
||||
}
|
||||
const { tag } = Astro.props
|
||||
---
|
||||
|
||||
<div class="badge badge-outline">{tag}</div>
|
12
src/components/badge/BadgeList.astro
Normal file
12
src/components/badge/BadgeList.astro
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
import Badge from "./Badge.astro"
|
||||
interface Props {
|
||||
tags: string[]
|
||||
}
|
||||
|
||||
const { tags } = Astro.props
|
||||
---
|
||||
|
||||
<div class="flex flex-wrap gap-1">
|
||||
{tags.map((tag) => <Badge tag={tag} />)}
|
||||
</div>
|
12
src/components/links/ExternalLink.astro
Normal file
12
src/components/links/ExternalLink.astro
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
interface Props {
|
||||
href: string
|
||||
class?: string
|
||||
}
|
||||
|
||||
const { href, class: clazz } = Astro.props
|
||||
---
|
||||
|
||||
<a href={href} target="_blank" rel="noopener" class:list={["link", clazz]}>
|
||||
<slot />
|
||||
</a>
|
16
src/components/links/GiteaLink.astro
Normal file
16
src/components/links/GiteaLink.astro
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
import ExternalLink from "./ExternalLink.astro"
|
||||
import * as m from "../../paraglide/messages"
|
||||
import Gitea from "../../icons/Gitea.astro"
|
||||
interface Props {
|
||||
href: string
|
||||
}
|
||||
const { href } = Astro.props
|
||||
---
|
||||
|
||||
<div>
|
||||
<ExternalLink href={href} class="flex items-center gap-1">
|
||||
<Gitea class="w-6 h-6" />
|
||||
{m.sourceCode()}
|
||||
</ExternalLink>
|
||||
</div>
|
18
src/content/config.ts
Normal file
18
src/content/config.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { defineCollection, z } from "astro:content"
|
||||
const projectCollection = defineCollection({
|
||||
type: "content",
|
||||
schema: ({ image }) =>
|
||||
z.object({
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
heroImage: image(),
|
||||
heroImageAlt: z.string(),
|
||||
tags: z.array(z.string()),
|
||||
source: z.string(),
|
||||
createdAt: z.string(),
|
||||
updatedAt: z.string(),
|
||||
}),
|
||||
})
|
||||
export const collections = {
|
||||
projects: projectCollection,
|
||||
}
|
14
src/content/projects/homepage.mdx
Normal file
14
src/content/projects/homepage.mdx
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
title: "Hjemmeside"
|
||||
description: "Hjemmesiden"
|
||||
heroImage: "./kevin-james.jpg"
|
||||
heroImageAlt: "The homepage of this site"
|
||||
tags: [Astro, Svelte, TypeScript, I18n]
|
||||
source: "https://example.com"
|
||||
createdAt: "2024-09-22"
|
||||
updatedAt: "2024-09-22"
|
||||
---
|
||||
|
||||
This is a short meta post about the homepage of this site.
|
||||
It is a simple landing page with a short introduction to the site and a list of the latest posts.
|
||||
The site is not built with GatsbyJS and Contentful.
|
12
src/content/projects/hotelService.mdx
Normal file
12
src/content/projects/hotelService.mdx
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
title: "Hotel Service"
|
||||
description: "REST API for managing hotels"
|
||||
heroImage: "./kevin-james.jpg"
|
||||
heroImageAlt: "The homepage of this site"
|
||||
tags: [Rust, Axum, Postgres, REST]
|
||||
source: "https://example.com"
|
||||
createdAt: "2024-09-22"
|
||||
updatedAt: "2024-09-22"
|
||||
---
|
||||
|
||||
Hello
|
BIN
src/content/projects/kevin-james.jpg
Normal file
BIN
src/content/projects/kevin-james.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 364 KiB |
12
src/env.d.ts
vendored
12
src/env.d.ts
vendored
@ -1 +1,11 @@
|
||||
/// <reference path="../.astro/types.d.ts" />
|
||||
/// <reference path="../.astro/types.d.ts" />
|
||||
/// <reference types="astro/client" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly URL: string
|
||||
readonly GIT_URL: string
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv
|
||||
}
|
||||
|
9
src/icons/ExternalLink.astro
Normal file
9
src/icons/ExternalLink.astro
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
// By GitLab SVGs
|
||||
const props = Astro.props
|
||||
---
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16" {...props}>
|
||||
<path fill="currentColor" fill-rule="evenodd"
|
||||
d="M10.75 1a.75.75 0 0 0 0 1.5h1.69L8.22 6.72a.75.75 0 0 0 1.06 1.06l4.22-4.22v1.69a.75.75 0 0 0 1.5 0V1zM2.5 4v9a.5.5 0 0 0 .5.5h9a.5.5 0 0 0 .5-.5V8.75a.75.75 0 0 1 1.5 0V13a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h4.25a.75.75 0 0 1 0 1.5H3a.5.5 0 0 0-.5.5"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
9
src/icons/GitHub.astro
Normal file
9
src/icons/GitHub.astro
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
// By GitLab SVGs
|
||||
const props = Astro.props
|
||||
---
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16" {...props}>
|
||||
<path fill="currentColor" fill-rule="evenodd"
|
||||
d="M7.976 0A7.977 7.977 0 0 0 0 7.976c0 3.522 2.3 6.507 5.431 7.584c.392.049.538-.196.538-.392v-1.37c-2.201.49-2.69-1.076-2.69-1.076c-.343-.93-.881-1.175-.881-1.175c-.734-.489.048-.489.048-.489c.783.049 1.224.832 1.224.832c.734 1.223 1.859.88 2.3.685c.048-.538.293-.88.489-1.076c-1.762-.196-3.621-.881-3.621-3.964c0-.88.293-1.566.832-2.153c-.05-.147-.343-.978.098-2.055c0 0 .685-.196 2.201.832c.636-.196 1.322-.245 2.007-.245s1.37.098 2.006.245c1.517-1.027 2.202-.832 2.202-.832c.44 1.077.146 1.908.097 2.104a3.16 3.16 0 0 1 .832 2.153c0 3.083-1.86 3.719-3.62 3.915c.293.244.538.733.538 1.467v2.202c0 .196.146.44.538.392A7.98 7.98 0 0 0 16 7.976C15.951 3.572 12.38 0 7.976 0"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
9
src/icons/Gitea.astro
Normal file
9
src/icons/Gitea.astro
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
// By GitLab SVGs
|
||||
const props = Astro.props
|
||||
---
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16" {...props}>
|
||||
<path fill="currentColor" fill-rule="evenodd"
|
||||
d="M15.46 3.206s.14-.003.245.102l.01.01c.064.06.258.244.285 1.074c0 2.902-1.405 5.882-1.405 5.882a9 9 0 0 1-.359.713c-.458.802-.786 1.153-.786 1.153s-.318.379-.675.595c-.415.265-.72.263-.72.263L7.247 13c-.636-.079-1.29-.736-1.927-1.578c-.47-.677-.779-1.413-.779-1.413s-2.51.034-3.675-1.394C.235 7.895.103 7.067.06 6.769q0-.012-.004-.029c-.05-.324-.285-1.873.821-2.86c.517-.496 1.148-.638 1.37-.684c.371-.081.667-.06.903-.044l.09.006c.391.035 3.99.216 3.99.216s1.532.066 2.27.056c0 0 .003 1.853.003 2.78q.105.048.211.1l.212.1V3.427q.494-.005.996-.017h.011c1.545-.036 4.528-.204 4.528-.204ZM2.113 8.026s.28.26.94.477c.43.152 1.094.231 1.094.231S3.699 7.5 3.516 6.757c-.22-.886-.4-2.398-.4-2.398s-.438-.015-.789.079c-.766.19-.98.763-.98.763s-.384.688.036 1.813c.244.672.73 1.013.73 1.013Zm8.084 3.607c.344-.023.499-.392.499-.392s1.24-2.486 1.4-2.878a.7.7 0 0 0 .046-.438c-.07-.267-.39-.412-.39-.412l-1.926-.935l-.165.339l-.18.369a.46.46 0 0 1 .128.341s.433.186.743.387c0 0 .257.135.32.425c.075.273-.04.488-.066.539l-.002.003s-.216.51-.343.774l-.004.007q-.07.144-.139.28a.454.454 0 1 1-.32-.15s.41-.84.468-1.033c0 0 .096-.24.048-.38a.47.47 0 0 0-.19-.188a6 6 0 0 0-.678-.34s-.076.068-.18.09a.5.5 0 0 1-.158.014l-.611 1.25a.46.46 0 0 1 .046.587a.46.46 0 0 1-.578.138a.46.46 0 0 1-.232-.51a.46.46 0 0 1 .44-.35L8.8 7.886a.457.457 0 0 1 .361-.744l.185-.375l.167-.341l-.579-.281s-.251-.125-.458-.072a.6.6 0 0 0-.114.039c-.189.084-.31.33-.31.33L6.668 9.293s-.124.254-.068.46c.048.252.325.397.325.397l2.874 1.4l.135.054s.114.04.262.03Z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
9
src/icons/Hamburger.astro
Normal file
9
src/icons/Hamburger.astro
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
// By GitLab SVGs
|
||||
const props = Astro.props
|
||||
---
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16" {...props}>
|
||||
<path fill="currentColor" fill-rule="evenodd"
|
||||
d="M0 3.75A.75.75 0 0 1 .75 3h14.5a.75.75 0 0 1 0 1.5H.75A.75.75 0 0 1 0 3.75M0 8a.75.75 0 0 1 .75-.75h14.5a.75.75 0 0 1 0 1.5H.75A.75.75 0 0 1 0 8m.75 3.5a.75.75 0 0 0 0 1.5h14.5a.75.75 0 0 0 0-1.5z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
9
src/icons/LinkedIn.astro
Normal file
9
src/icons/LinkedIn.astro
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
// By GitLab SVGs
|
||||
const props = Astro.props
|
||||
---
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16" {...props}>
|
||||
<path fill="currentColor" fill-rule="evenodd"
|
||||
d="M3 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2zm1.102 4.297a1.195 1.195 0 1 0 0-2.39a1.195 1.195 0 0 0 0 2.39m1 7.516V6.234h-2v6.579zM6.43 6.234h2v.881c.295-.462.943-1.084 2.148-1.084c1.438 0 2.219.953 2.219 2.766c0 .087.008.484.008.484v3.531h-2v-3.53c0-.485-.102-1.438-1.18-1.438c-1.079 0-1.17 1.198-1.195 1.982v2.986h-2z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
8
src/icons/Mastodon.astro
Normal file
8
src/icons/Mastodon.astro
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
// By GitLab SVGs
|
||||
const props = Astro.props
|
||||
---
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16" {...props}>
|
||||
<path fill="currentColor"
|
||||
d="M15.498 3.706C15.264 1.986 13.749.632 11.954.37C11.65.325 10.503.164 7.844.164h-.02c-2.66 0-3.23.161-3.533.206C2.546.625.951 1.842.565 3.582C.379 4.438.359 5.388.393 6.259C.443 7.51.453 8.756.567 10q.12 1.242.414 2.454c.368 1.49 1.856 2.731 3.314 3.237a9 9 0 0 0 4.848.253q.265-.06.525-.141c.39-.123.849-.26 1.186-.502l.01-.013l.005-.016v-1.206l-.004-.015a.04.04 0 0 0-.024-.02h-.016c-1.03.244-2.087.366-3.146.364c-1.824 0-2.314-.856-2.455-1.212a3.7 3.7 0 0 1-.213-.955a.035.035 0 0 1 .028-.036h.016a13.3 13.3 0 0 0 3.095.364q.375.002.751-.007c1.049-.03 2.154-.082 3.186-.281l.073-.016c1.627-.31 3.176-1.28 3.333-3.736c.006-.097.02-1.013.02-1.113c.002-.342.112-2.42-.015-3.697m-2.505 6.13h-1.71V5.69c0-.873-.368-1.318-1.116-1.318c-.822 0-1.234.526-1.234 1.566v2.27h-1.7v-2.27c0-1.04-.413-1.566-1.235-1.566c-.744 0-1.115.445-1.116 1.318v4.145h-1.71v-4.27q0-1.31.677-2.08c.464-.513 1.074-.776 1.83-.776q1.316 0 1.979.999l.426.706l.426-.706c.441-.666 1.103-.999 1.977-.999c.756 0 1.366.263 1.832.776q.675.77.676 2.08z" />
|
||||
</svg>
|
BIN
src/images/me.jpg
Normal file
BIN
src/images/me.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 458 KiB |
@ -1,50 +1,31 @@
|
||||
---
|
||||
import Navbar from "../components/Navbar.astro"
|
||||
interface Props {
|
||||
title: string;
|
||||
title: string
|
||||
class?: string
|
||||
}
|
||||
const { title, class: clazz } = Astro.props
|
||||
|
||||
const { title } = Astro.props;
|
||||
import { languageTag } from "../paraglide/runtime"
|
||||
import Footer from "../components/Footer.astro"
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="description" content="Astro description" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<title>{title}</title>
|
||||
</head>
|
||||
<body>
|
||||
<slot />
|
||||
</body>
|
||||
<html lang={languageTag()} dir={"ltr"}>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="description" content="Astro description" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<link rel="sitemap" href="/sitemap-index.xml" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<title>{title}</title>
|
||||
</head>
|
||||
<body class="flex flex-col h-screen">
|
||||
<Navbar />
|
||||
<main class:list={["grow", clazz]}>
|
||||
<slot />
|
||||
</main>
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
<style is:global>
|
||||
:root {
|
||||
--accent: 136, 58, 234;
|
||||
--accent-light: 224, 204, 250;
|
||||
--accent-dark: 49, 10, 101;
|
||||
--accent-gradient: linear-gradient(
|
||||
45deg,
|
||||
rgb(var(--accent)),
|
||||
rgb(var(--accent-light)) 30%,
|
||||
white 60%
|
||||
);
|
||||
}
|
||||
html {
|
||||
font-family: system-ui, sans-serif;
|
||||
background: #13151a;
|
||||
}
|
||||
code {
|
||||
font-family:
|
||||
Menlo,
|
||||
Monaco,
|
||||
Lucida Console,
|
||||
Liberation Mono,
|
||||
DejaVu Sans Mono,
|
||||
Bitstream Vera Sans Mono,
|
||||
Courier New,
|
||||
monospace;
|
||||
}
|
||||
</style>
|
||||
|
23
src/links.ts
Normal file
23
src/links.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import * as m from "./paraglide/messages.js"
|
||||
|
||||
interface Link {
|
||||
label: string
|
||||
to: string
|
||||
}
|
||||
|
||||
const Links: Link[] = [
|
||||
{
|
||||
label: m.home(),
|
||||
to: "/",
|
||||
},
|
||||
{
|
||||
label: m.myProjects(),
|
||||
to: "/project",
|
||||
},
|
||||
{
|
||||
label: m.contactMe(),
|
||||
to: "/contact-me",
|
||||
},
|
||||
]
|
||||
|
||||
export default Links
|
8
src/pages/404.astro
Normal file
8
src/pages/404.astro
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro"
|
||||
---
|
||||
|
||||
<Layout title="404">
|
||||
<h1 class="text-4xl font-bold text-center my-10">404</h1>
|
||||
<p class="text-center">This page does not exist.</p>
|
||||
</Layout>
|
11
src/pages/contact-me.astro
Normal file
11
src/pages/contact-me.astro
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
// TODO form
|
||||
// TODO self-host email server
|
||||
import "../styles/global.css"
|
||||
import ContactMeForm from "../components/ContactMeForm.astro"
|
||||
import Layout from "../layouts/Layout.astro"
|
||||
---
|
||||
|
||||
<Layout title="Kontakt meg">
|
||||
<ContactMeForm client:load />
|
||||
</Layout>
|
3
src/pages/en/contact-me.astro
Normal file
3
src/pages/en/contact-me.astro
Normal file
@ -0,0 +1,3 @@
|
||||
---
|
||||
|
||||
---
|
3
src/pages/en/hardware.astro
Normal file
3
src/pages/en/hardware.astro
Normal file
@ -0,0 +1,3 @@
|
||||
---
|
||||
|
||||
---
|
8
src/pages/en/index.astro
Normal file
8
src/pages/en/index.astro
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
import OnePager from "../../components/Greeting.astro"
|
||||
import Layout from "../../layouts/Layout.astro"
|
||||
---
|
||||
|
||||
<Layout title="Welcome">
|
||||
<OnePager />
|
||||
</Layout>
|
3
src/pages/en/links.astro
Normal file
3
src/pages/en/links.astro
Normal file
@ -0,0 +1,3 @@
|
||||
---
|
||||
|
||||
---
|
15
src/pages/en/project/[project].astro
Normal file
15
src/pages/en/project/[project].astro
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
import ProjectPage from "../../../components/ProjectPage.astro"
|
||||
import { type GetStaticPathsResult } from "astro"
|
||||
|
||||
export function getStaticPaths(): GetStaticPathsResult {
|
||||
return [
|
||||
{ params: { project: "hotelservice" } },
|
||||
{ params: { project: "homepage" } },
|
||||
]
|
||||
}
|
||||
|
||||
const { project } = Astro.params
|
||||
---
|
||||
|
||||
<ProjectPage project={project as string} />
|
5
src/pages/en/project/index.astro
Normal file
5
src/pages/en/project/index.astro
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
<p>These are all my projects</p>
|
10
src/pages/hardware.astro
Normal file
10
src/pages/hardware.astro
Normal file
@ -0,0 +1,10 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro"
|
||||
import HardwarePage from "../components/HardwarePage.svelte"
|
||||
import "../styles/global.css"
|
||||
---
|
||||
|
||||
<Layout title="Hardware" class="mx-auto max-w-[750px]">
|
||||
<h1 class="text-center">Hardware</h1>
|
||||
<HardwarePage client:load />
|
||||
</Layout>
|
@ -1,123 +1,21 @@
|
||||
---
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
import Card from '../components/Card.astro';
|
||||
import Layout from "../layouts/Layout.astro"
|
||||
import OnePager from "../components/Greeting.astro"
|
||||
---
|
||||
|
||||
<Layout title="Welcome to Astro.">
|
||||
<main>
|
||||
<svg
|
||||
class="astro-a"
|
||||
width="495"
|
||||
height="623"
|
||||
viewBox="0 0 495 623"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M167.19 364.254C83.4786 364.254 0 404.819 0 404.819C0 404.819 141.781 19.4876 142.087 18.7291C146.434 7.33701 153.027 0 162.289 0H332.441C341.703 0 348.574 7.33701 352.643 18.7291C352.92 19.5022 494.716 404.819 494.716 404.819C494.716 404.819 426.67 364.254 327.525 364.254L264.41 169.408C262.047 159.985 255.147 153.581 247.358 153.581C239.569 153.581 232.669 159.985 230.306 169.408L167.19 364.254ZM160.869 530.172C160.877 530.18 160.885 530.187 160.894 530.195L160.867 530.181C160.868 530.178 160.868 530.175 160.869 530.172ZM136.218 411.348C124.476 450.467 132.698 504.458 160.869 530.172C160.997 529.696 161.125 529.242 161.248 528.804C161.502 527.907 161.737 527.073 161.917 526.233C165.446 509.895 178.754 499.52 195.577 500.01C211.969 500.487 220.67 508.765 223.202 527.254C224.141 534.12 224.23 541.131 224.319 548.105C224.328 548.834 224.337 549.563 224.347 550.291C224.563 566.098 228.657 580.707 237.264 593.914C245.413 606.426 256.108 615.943 270.749 622.478C270.593 621.952 270.463 621.508 270.35 621.126C270.045 620.086 269.872 619.499 269.685 618.911C258.909 585.935 266.668 563.266 295.344 543.933C298.254 541.971 301.187 540.041 304.12 538.112C310.591 533.854 317.059 529.599 323.279 525.007C345.88 508.329 360.09 486.327 363.431 457.844C364.805 446.148 363.781 434.657 359.848 423.275C358.176 424.287 356.587 425.295 355.042 426.275C351.744 428.366 348.647 430.33 345.382 431.934C303.466 452.507 259.152 455.053 214.03 448.245C184.802 443.834 156.584 436.019 136.218 411.348Z"
|
||||
fill="url(#paint0_linear_1805_24383)"></path>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="paint0_linear_1805_24383"
|
||||
x1="247.358"
|
||||
y1="0"
|
||||
x2="247.358"
|
||||
y2="622.479"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-opacity="0.9"></stop>
|
||||
<stop offset="1" stop-opacity="0.2"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<h1>Welcome to <span class="text-gradient">Astro</span></h1>
|
||||
<p class="instructions">
|
||||
To get started, open the directory <code>src/pages</code> in your project.<br />
|
||||
<strong>Code Challenge:</strong> Tweak the "Welcome to Astro" message above.
|
||||
</p>
|
||||
<ul role="list" class="link-card-grid">
|
||||
<Card
|
||||
href="https://docs.astro.build/"
|
||||
title="Documentation"
|
||||
body="Learn how Astro works and explore the official API docs."
|
||||
/>
|
||||
<Card
|
||||
href="https://astro.build/integrations/"
|
||||
title="Integrations"
|
||||
body="Supercharge your project with new frameworks and libraries."
|
||||
/>
|
||||
<Card
|
||||
href="https://astro.build/themes/"
|
||||
title="Themes"
|
||||
body="Explore a galaxy of community-built starter themes."
|
||||
/>
|
||||
<Card
|
||||
href="https://astro.build/chat/"
|
||||
title="Community"
|
||||
body="Come say hi to our amazing Discord community. ❤️"
|
||||
/>
|
||||
</ul>
|
||||
</main>
|
||||
<!--
|
||||
One-pager
|
||||
- Quick overview of me.
|
||||
- Image
|
||||
- Contact
|
||||
- Email
|
||||
- LinkedIn
|
||||
- GitHub
|
||||
- Education
|
||||
- Experience
|
||||
- Skills
|
||||
- Certifications
|
||||
-->
|
||||
<Layout title="Velkommen">
|
||||
<OnePager />
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
main {
|
||||
margin: auto;
|
||||
padding: 1rem;
|
||||
width: 800px;
|
||||
max-width: calc(100% - 2rem);
|
||||
color: white;
|
||||
font-size: 20px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.astro-a {
|
||||
position: absolute;
|
||||
top: -32px;
|
||||
left: 50%;
|
||||
transform: translatex(-50%);
|
||||
width: 220px;
|
||||
height: auto;
|
||||
z-index: -1;
|
||||
}
|
||||
h1 {
|
||||
font-size: 4rem;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
.text-gradient {
|
||||
background-image: var(--accent-gradient);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-size: 400%;
|
||||
background-position: 0%;
|
||||
}
|
||||
.instructions {
|
||||
margin-bottom: 2rem;
|
||||
border: 1px solid rgba(var(--accent-light), 25%);
|
||||
background: linear-gradient(rgba(var(--accent-dark), 66%), rgba(var(--accent-dark), 33%));
|
||||
padding: 1.5rem;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.instructions code {
|
||||
font-size: 0.8em;
|
||||
font-weight: bold;
|
||||
background: rgba(var(--accent-light), 12%);
|
||||
color: rgb(var(--accent-light));
|
||||
border-radius: 4px;
|
||||
padding: 0.3em 0.4em;
|
||||
}
|
||||
.instructions strong {
|
||||
color: rgb(var(--accent-light));
|
||||
}
|
||||
.link-card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(24ch, 1fr));
|
||||
gap: 2rem;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
|
3
src/pages/links.astro
Normal file
3
src/pages/links.astro
Normal file
@ -0,0 +1,3 @@
|
||||
---
|
||||
|
||||
---
|
15
src/pages/project/[project].astro
Normal file
15
src/pages/project/[project].astro
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
import ProjectPage from "../../components/ProjectPage.astro"
|
||||
import { type GetStaticPathsResult } from "astro"
|
||||
|
||||
export function getStaticPaths(): GetStaticPathsResult {
|
||||
return [
|
||||
{ params: { project: "hotelservice" } },
|
||||
{ params: { project: "homepage" } },
|
||||
]
|
||||
}
|
||||
|
||||
const { project } = Astro.params
|
||||
---
|
||||
|
||||
<ProjectPage project={project as string} />
|
32
src/pages/project/index.astro
Normal file
32
src/pages/project/index.astro
Normal file
@ -0,0 +1,32 @@
|
||||
---
|
||||
import { getCollection } from "astro:content"
|
||||
import ProjectCard from "../../components/ProjectCard.astro"
|
||||
import Layout from "../../layouts/Layout.astro"
|
||||
|
||||
const projects = await getCollection("projects")
|
||||
---
|
||||
|
||||
<Layout title="Projects">
|
||||
<h1 class="text-4xl font-bold text-center my-10">Projects</h1>
|
||||
<div class="flex flex-wrap justify-around">
|
||||
{
|
||||
projects.map(
|
||||
({
|
||||
data: { title, description, tags, heroImage, heroImageAlt },
|
||||
slug,
|
||||
}) => (
|
||||
<div class="my-5">
|
||||
<ProjectCard
|
||||
title={title}
|
||||
linkTo={`/project/${slug}`}
|
||||
description={description}
|
||||
tags={tags}
|
||||
image={heroImage}
|
||||
imageAlt={heroImageAlt}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</Layout>
|
13
src/pages/robots.txt.ts
Normal file
13
src/pages/robots.txt.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import type { APIRoute } from "astro"
|
||||
|
||||
const getRobotsTxt = (sitemapURL: URL) => `
|
||||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
Sitemap: ${sitemapURL.href}
|
||||
`
|
||||
|
||||
export const GET: APIRoute = ({ site }) => {
|
||||
const sitemapURL = new URL("sitemap-index.xml", site)
|
||||
return new Response(getRobotsTxt(sitemapURL))
|
||||
}
|
15
src/styles/global.css
Normal file
15
src/styles/global.css
Normal file
@ -0,0 +1,15 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer utilities {
|
||||
.debug {
|
||||
@apply border border-red-500;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
h1 {
|
||||
@apply text-4xl font-bold mb-2;
|
||||
}
|
||||
}
|
5
svelte.config.js
Normal file
5
svelte.config.js
Normal file
@ -0,0 +1,5 @@
|
||||
import { vitePreprocess } from "@astrojs/svelte"
|
||||
|
||||
export default {
|
||||
preprocess: vitePreprocess(),
|
||||
}
|
8
tailwind.config.mjs
Normal file
8
tailwind.config.mjs
Normal file
@ -0,0 +1,8 @@
|
||||
/** @type {import("tailwindcss").Config} */
|
||||
export default {
|
||||
content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"],
|
||||
theme: {
|
||||
extend: {}
|
||||
},
|
||||
plugins: [require("@tailwindcss/typography"), require("daisyui")]
|
||||
}
|
@ -1,3 +1,11 @@
|
||||
{
|
||||
"extends": "astro/tsconfigs/strict"
|
||||
}
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"include": ["src/**/*.ts", "src/**/*.astro", "src/**/*.svelte"],
|
||||
"compilerOptions": {
|
||||
"strictNullChecks": true,
|
||||
"allowJs": true,
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user