Private
Public Access
1
0

feat: add contact component

- update css
- update data.json
- update types
This commit is contained in:
2026-02-03 19:45:55 +01:00
parent 20f8d06091
commit 16237ad80a
5 changed files with 200 additions and 2 deletions

View File

@@ -0,0 +1,154 @@
<script lang="ts">
import type { Contact } from '$lib/types/data';
let { title, anchorId, subtitle, form }: Contact = $props();
let emailData = $state({
to: "frodo06@gmx.de",
subject: "",
message: ""
});
function handleSubmit(event: SubmitEvent) {
event.preventDefault();
const subject = encodeURIComponent(emailData.subject);
const body = encodeURIComponent(emailData.message);
// Construct the mailto URL
const mailtoUrl = `mailto:${emailData.to}?subject=${subject}&body=${body}`;
// Open the user's email client
window.location.href = mailtoUrl;
}
</script>
<section id={anchorId}>
<h2>{title}</h2>
{#if (subtitle)}
<p>{subtitle}</p>
{/if}
<form onsubmit={handleSubmit} class="flex flex-col gap-4">
<p>{form.title}</p>
<input
bind:value={emailData.subject}
placeholder={form.subjectPlaceholder}
required
/>
<textarea
bind:value={emailData.message}
placeholder={form.messagePlaceholder}
rows="5"
></textarea>
<button type="submit">
{form.submit}
</button>
</form>
</section>
<style>
section {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 1rem;
padding: 5rem 1rem;
@media (min-width: 768px) {
padding: 6rem 2.5rem;
}
}
h2 {
margin: unset;
font-size: 1.875rem;
text-align: center;
color: transparent;
background-image: var(--color-headline-background-image);
background-clip: text;
@media (min-width: 768px) {
font-size: 3rem;
}
}
p {
margin: unset;
margin-bottom: 2rem;
font-size: 1.125rem;
text-align: center;
color: var(--color-text-secondary);
@media (min-width: 768px) {
font-size: 1.25rem;
}
}
form {
display: flex;
flex-direction: column;
align-items: stretch;
justify-content: center;
gap: 1rem;
max-width: 32rem;
width: 100%;
padding: 2rem;
background-color: var(--color-contact-background);
border: 1px solid var(--color-contact-background-border);
border-radius: 1rem;
color: var(--color-contact-text);
p {
margin-bottom: 1rem;
color: var(--color-contact-text);
font-size: 1.25rem;
font-weight: bold;
@media (min-width: 768px) {
font-size: 1.5rem;
}
}
input, textarea {
padding: 1rem;
color: white;
font-size: 1rem;
line-height: 1.5;
background-color: var(--color-contact-background-input);
border: 1px solid var(--color-contact-background-border);
border-radius: 0.75rem;
&:focus {
outline: none;
border: 1px solid var(--color-contact-background-input-border-focused);
}
}
button {
appearance: none;
padding: 1rem 1.5rem;
background-image: var(--color-contact-background-button);
border: unset;
border-radius: 0.75rem;
color: var(--color-contact-text);
font-size: 1.125rem;
font-weight: bold;
line-height: 1.5;
transition: translate ease-in-out 0.2s;
&:hover {
background-image: var(--color-contact-background-button-hover);
translate: 0 -0.25rem;
}
}
}
</style>

View File

@@ -184,5 +184,16 @@
"items": ["SEO", "Accessibility", "KI-basierte Tools"]
}
]
},
"contact": {
"title": "Kontakt",
"anchorId": "contact",
"subtitle": "Hinterlassen Sie mir eine Nachricht",
"form": {
"title": "Schneller Kontakt",
"subjectPlaceholder": "Dein Name",
"messagePlaceholder": "Deine Nachricht",
"submit": "Email öffnen"
}
}
}

View File

@@ -47,6 +47,22 @@
--color-card-box-shadow: 0 0 0 0 rgba(0, 0, 0, 0), oklab(0.623 -0.0378409 -0.210628 / 0.5) 0 25px 50px -12px;
--color-contact-background: oklch(0.278 0.033 256.848);
--color-contact-background-border: oklab(0.623 -0.0378409 -0.210628 / 0.3);
--color-contact-text: oklch(0.967 0.003 264.542);
--color-contact-background-input: oklch(0.373 0.034 259.733);
--color-contact-background-input-border-focused: oklch(70.7% 0.165 254.624);
--color-contact-background-button: linear-gradient(
to right,
oklch(0.623 0.214 259.815) 0%,
oklch(0.511 0.262 276.966) 100%
);
--color-contact-background-button-hover: linear-gradient(
to right,
oklch(0.546 0.245 262.881) 0%,
oklch(0.457 0.24 277.023) 100%
);
--z-index-header: 50;
}

View File

@@ -6,6 +6,7 @@ export type Data = {
profile: Profile;
projects: Projects;
skills: Skills;
contact: Contact;
};
export type Header = {
@@ -60,8 +61,22 @@ export type Skills = {
export type SkillEntry = {
title: string;
icon: LucideIconName;
icon: string;
items: string[];
};
export type LucideIconName = keyof typeof icons;
export type Contact = {
title: string;
subtitle: string;
anchorId: string;
form: ContactForm;
};
export type ContactForm = {
title: string;
subjectPlaceholder: string;
messagePlaceholder: string;
submit: string;
};

View File

@@ -4,6 +4,7 @@
import Profile from '$lib/components/Profile.svelte';
import Projects from '$lib/components/Projects.svelte';
import Skills from '$lib/components/Skills.svelte';
import Contact from '$lib/components/Contact.svelte';
let data: Data = content;
</script>
@@ -11,3 +12,4 @@
<Profile {...data.profile} />
<Projects {...data.projects} />
<Skills {...data.skills} />
<Contact {...data.contact} />