Add Bytemalte projects showcase website with file-based CMS

- Svelte 5 website following BYTEMALTE design system
- File-based CMS via static/projects.json (no restart needed)
- Hero section, responsive project cards grid, navbar, footer
- All 8 projects integrated with Code/Download links
This commit is contained in:
2026-03-14 23:05:44 +01:00
commit e74feec5ea
26 changed files with 2114 additions and 0 deletions

23
.gitignore vendored Normal file
View File

@@ -0,0 +1,23 @@
node_modules
# Output
.output
.vercel
.netlify
.wrangler
/.svelte-kit
/build
# OS
.DS_Store
Thumbs.db
# Env
.env
.env.*
!.env.example
!.env.test
# Vite
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
engine-strict=true

3
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"recommendations": ["svelte.svelte-vscode"]
}

57
BYTEMALTE.md Normal file
View File

@@ -0,0 +1,57 @@
# 🎨 BYTEMALTE Design System v1.1
Dieses Dokument definiert die visuelle Identität für alle Projekte von **ByteMalte**. Konsistenz vor Komplexität.
---
## 🌈 Farbpalette (Hex)
| Rolle | Hex-Code | Kontrast (auf `#0F172A`) | Einsatzbereich |
| :--- | :--- | :--- | :--- |
| **Primary** | `#8888FF` | 5.7:1 | Buttons, Links, Brand-Elemente |
| **Secondary** | `#3DDC84` | 7.0:1 | Success-Meldungen, Akzente, Tags |
| **Background** | `#0F172A` | — | Haupt-Hintergrund (Dark Mode bevorzugt) |
| **Surface** | `#1E293B` | — | Karten, Sektionen, Modals |
| **Text (High)** | `#F8FAFC` | 15.4:1 | Überschriften, Fließtext |
| **Text (Muted)** | `#B0BDD0` | 7.2:1 | Beschreibungen, Footer, sekundärer Text |
| **Accent/Error** | `#EF4444` | 4.5:1 | Fehler, Löschen-Buttons |
> Alle Farben erfüllen WCAG AA (mindestens 4.5:1 für normalen Text, 3:1 für großen Text).
---
## 📐 Layout & Abrundungen (Borders)
Wir nutzen ein weiches, modernes Design mit großzügigen Radien.
* **Buttons:** `8px` (Medium Round)
* **Cards / Container:** `16px` (Large Round)
* **Inputs / Formulare:** `8px`
* **Profilbilder:** `50%` (Circle)
---
## 🖋️ Typografie
* **Font Family:** `Inter, system-ui, sans-serif` (Clean & Programmierer-Vibe)
* **Headline Scale:**
* **h1:** `2.5rem` | Bold (700)
* **h2:** `1.8rem` | Semi-Bold (600)
* **h3:** `1.2rem` | Medium (500)
* **Body:** `1rem` | Regular (400) | Line-height: `1.6`
---
## ⚡ UI-Komponenten Spezifikationen
### Buttons
* **Default:** Primary Background, White Text, keine Border.
* **Hover:** Helligkeit +10%, leichter Box-Shadow (`0 4px 6px -1px rgb(0 0 0 / 0.1)`).
* **Active:** Skalierung auf `0.98` (Click-Effekt).
### Karten (Cards)
* **Background:** `Surface` (`#1E293B`)
* **Border:** `1px solid #334155`
* **Padding:** `24px`
---

42
README.md Normal file
View File

@@ -0,0 +1,42 @@
# sv
Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
## Creating a project
If you're seeing this, you've probably already done this step. Congrats!
```sh
# create a new project
npx sv create my-app
```
To recreate this project with the same configuration:
```sh
# recreate this project
pnpm dlx sv@0.12.7 create --template minimal --types ts --install pnpm bytemalte_de_svelte
```
## Developing
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
```sh
npm run dev
# or start the server and open the app in a new browser tab
npm run dev -- --open
```
## Building
To create a production version of your app:
```sh
npm run build
```
You can preview the production build with `npm run preview`.
> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.

8
justfile Normal file
View File

@@ -0,0 +1,8 @@
default:
just --list
run:
pnpm run dev --open
build:
pnpm run build

23
package.json Normal file
View File

@@ -0,0 +1,23 @@
{
"name": "bytemalte-de-svelte",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"prepare": "svelte-kit sync || echo ''",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^7.0.0",
"@sveltejs/kit": "^2.50.2",
"@sveltejs/vite-plugin-svelte": "^6.2.4",
"svelte": "^5.51.0",
"svelte-check": "^4.4.2",
"typescript": "^5.9.3",
"vite": "^7.3.1"
}
}

1026
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

2
pnpm-workspace.yaml Normal file
View File

@@ -0,0 +1,2 @@
onlyBuiltDependencies:
- esbuild

91
src/app.css Normal file
View File

@@ -0,0 +1,91 @@
:root {
/* Color Palette - BYTEMALTE Design System */
--color-primary: #8888ff;
--color-secondary: #3ddc84;
--color-background: #0f172a;
--color-surface: #1e293b;
--color-surface-hover: #263548;
--color-border: #334155;
--color-text-high: #f8fafc;
--color-text-muted: #b0bdd0;
--color-error: #ef4444;
/* Typography */
--font-family: 'Inter', system-ui, sans-serif;
--font-size-h1: 2.5rem;
--font-size-h2: 1.8rem;
--font-size-h3: 1.2rem;
--font-size-body: 1rem;
--line-height: 1.6;
/* Borders */
--radius-button: 8px;
--radius-card: 16px;
--radius-input: 8px;
/* Spacing */
--section-padding: 5rem 2rem;
--container-width: 1200px;
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
scroll-behavior: smooth;
}
body {
font-family: var(--font-family);
font-size: var(--font-size-body);
line-height: var(--line-height);
background-color: var(--color-background);
color: var(--color-text-high);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a {
color: var(--color-primary);
text-decoration: none;
transition: color 0.2s ease;
}
a:hover {
color: #a0a0ff;
}
h1 {
font-size: var(--font-size-h1);
font-weight: 700;
line-height: 1.2;
}
h2 {
font-size: var(--font-size-h2);
font-weight: 600;
line-height: 1.3;
}
h3 {
font-size: var(--font-size-h3);
font-weight: 500;
line-height: 1.4;
}
img {
max-width: 100%;
height: auto;
display: block;
}
.container {
max-width: var(--container-width);
margin: 0 auto;
padding: 0 2rem;
}

13
src/app.d.ts vendored Normal file
View File

@@ -0,0 +1,13 @@
// See https://svelte.dev/docs/kit/types#app.d.ts
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface PageState {}
// interface Platform {}
}
}
export {};

16
src/app.html Normal file
View File

@@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="Bytemalte - Open source projects and community" />
<meta name="theme-color" content="#0f172a" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous" />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="107" height="128" viewBox="0 0 107 128"><title>svelte-logo</title><path d="M94.157 22.819c-10.4-14.885-30.94-19.297-45.792-9.835L22.282 29.608A29.92 29.92 0 0 0 8.764 49.65a31.5 31.5 0 0 0 3.108 20.231 30 30 0 0 0-4.477 11.183 31.9 31.9 0 0 0 5.448 24.116c10.402 14.887 30.942 19.297 45.791 9.835l26.083-16.624A29.92 29.92 0 0 0 98.235 78.35a31.53 31.53 0 0 0-3.105-20.232 30 30 0 0 0 4.474-11.182 31.88 31.88 0 0 0-5.447-24.116" style="fill:#ff3e00"/><path d="M45.817 106.582a20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.503 18 18 0 0 1 .624-2.435l.49-1.498 1.337.981a33.6 33.6 0 0 0 10.203 5.098l.97.294-.09.968a5.85 5.85 0 0 0 1.052 3.878 6.24 6.24 0 0 0 6.695 2.485 5.8 5.8 0 0 0 1.603-.704L69.27 76.28a5.43 5.43 0 0 0 2.45-3.631 5.8 5.8 0 0 0-.987-4.371 6.24 6.24 0 0 0-6.698-2.487 5.7 5.7 0 0 0-1.6.704l-9.953 6.345a19 19 0 0 1-5.296 2.326 20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.502 17.99 17.99 0 0 1 8.13-12.052l26.081-16.623a19 19 0 0 1 5.3-2.329 20.72 20.72 0 0 1 22.237 8.243 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-.624 2.435l-.49 1.498-1.337-.98a33.6 33.6 0 0 0-10.203-5.1l-.97-.294.09-.968a5.86 5.86 0 0 0-1.052-3.878 6.24 6.24 0 0 0-6.696-2.485 5.8 5.8 0 0 0-1.602.704L37.73 51.72a5.42 5.42 0 0 0-2.449 3.63 5.79 5.79 0 0 0 .986 4.372 6.24 6.24 0 0 0 6.698 2.486 5.8 5.8 0 0 0 1.602-.704l9.952-6.342a19 19 0 0 1 5.295-2.328 20.72 20.72 0 0 1 22.237 8.242 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-8.13 12.053l-26.081 16.622a19 19 0 0 1-5.3 2.328" style="fill:#fff"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,105 @@
<footer class="footer">
<div class="footer-container">
<div class="footer-brand">
<span class="footer-logo">Bytemalte</span>
<p>Open source projects and community</p>
</div>
<div class="footer-links">
<div class="footer-column">
<h4>Links</h4>
<a href="https://gitea.malxte.de/Bytemalte" target="_blank" rel="noopener noreferrer">Code Repository</a>
</div>
</div>
</div>
<div class="footer-bottom">
<p>&copy; {new Date().getFullYear()} Bytemalte. All rights reserved.</p>
</div>
</footer>
<style>
.footer {
background: var(--color-surface);
border-top: 1px solid var(--color-border);
padding: 3rem 2rem 1.5rem;
margin-top: 4rem;
}
.footer-container {
max-width: 1200px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: flex-start;
flex-wrap: wrap;
gap: 2rem;
margin-bottom: 2rem;
}
.footer-brand {
max-width: 300px;
}
.footer-logo {
font-size: 1.25rem;
font-weight: 700;
background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.footer-brand p {
color: var(--color-text-muted);
font-size: 0.875rem;
margin-top: 0.5rem;
}
.footer-links {
display: flex;
gap: 3rem;
}
.footer-column h4 {
font-size: 0.875rem;
font-weight: 600;
color: var(--color-text-high);
margin-bottom: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.footer-column a {
display: block;
color: var(--color-text-muted);
font-size: 0.875rem;
margin-bottom: 0.5rem;
transition: color 0.2s ease;
}
.footer-column a:hover {
color: var(--color-primary);
}
.footer-bottom {
max-width: 1200px;
margin: 0 auto;
padding-top: 1.5rem;
border-top: 1px solid var(--color-border);
}
.footer-bottom p {
color: var(--color-text-muted);
font-size: 0.8rem;
text-align: center;
}
@media (max-width: 640px) {
.footer-container {
flex-direction: column;
}
.footer-links {
gap: 2rem;
}
}
</style>

View File

@@ -0,0 +1,155 @@
<section class="hero">
<div class="hero-content">
<div class="hero-badge">My Projects</div>
<h1>
Building <span class="gradient-text">tools</span> for<br />
developers &amp; communities
</h1>
<p class="hero-description">
I'm, Bytemalte creat open source applications, libraries, and tools with Rust and modern web
technologies. Explore my projects below.
</p>
<div class="hero-actions">
<a href="#projects" class="btn btn-primary">View Projects</a>
<a
href="https://gitea.malxte.de/Bytemalte"
target="_blank"
rel="noopener noreferrer"
class="btn btn-secondary"
>
Browse Code
</a>
</div>
</div>
<div class="hero-glow"></div>
</section>
<style>
.hero {
position: relative;
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
padding: 8rem 2rem 4rem;
overflow: hidden;
}
.hero-content {
position: relative;
z-index: 1;
max-width: 800px;
text-align: center;
}
.hero-badge {
display: inline-block;
padding: 0.5rem 1rem;
border-radius: 9999px;
background: rgba(136, 136, 255, 0.15);
border: 1px solid rgba(136, 136, 255, 0.3);
color: var(--color-primary);
font-size: 0.875rem;
font-weight: 500;
margin-bottom: 1.5rem;
}
.hero h1 {
font-size: clamp(2.5rem, 5vw, 4rem);
font-weight: 700;
line-height: 1.1;
margin-bottom: 1.5rem;
}
.gradient-text {
background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.hero-description {
font-size: 1.125rem;
color: var(--color-text-muted);
max-width: 600px;
margin: 0 auto 2.5rem;
line-height: 1.7;
}
.hero-actions {
display: flex;
gap: 1rem;
justify-content: center;
flex-wrap: wrap;
}
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.875rem 2rem;
border-radius: var(--radius-button);
font-weight: 600;
font-size: 1rem;
transition: all 0.2s ease;
cursor: pointer;
text-decoration: none;
border: none;
}
.btn-primary {
background: var(--color-primary);
color: white;
}
.btn-primary:hover {
background: #9d9dff;
box-shadow: 0 4px 6px -1px rgba(136, 136, 255, 0.3);
transform: translateY(-1px);
}
.btn-primary:active {
transform: scale(0.98);
}
.btn-secondary {
background: var(--color-surface);
color: var(--color-text-high);
border: 1px solid var(--color-border);
}
.btn-secondary:hover {
background: var(--color-surface-hover);
border-color: var(--color-primary);
}
.hero-glow {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 600px;
height: 600px;
background: radial-gradient(
circle,
rgba(136, 136, 255, 0.08) 0%,
rgba(61, 220, 132, 0.04) 50%,
transparent 70%
);
pointer-events: none;
}
@media (max-width: 640px) {
.hero {
padding: 6rem 1.5rem 3rem;
}
.hero-actions {
flex-direction: column;
}
.btn {
width: 100%;
}
}
</style>

View File

@@ -0,0 +1,91 @@
<script lang="ts">
let scrolled = $state(false);
function handleScroll() {
scrolled = window.scrollY > 20;
}
</script>
<svelte:window onscroll={handleScroll} />
<nav class="navbar" class:scrolled>
<div class="nav-container">
<a href="/" class="logo">
<span class="logo-text">Bytemalte</span>
</a>
<div class="nav-links">
<a href="#projects">Projects</a>
<a href="https://gitea.malxte.de/Bytemalte" target="_blank" rel="noopener noreferrer">Code</a>
</div>
</div>
</nav>
<style>
.navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
padding: 1rem 2rem;
transition: all 0.3s ease;
background: transparent;
}
.navbar.scrolled {
background: rgba(15, 23, 42, 0.95);
backdrop-filter: blur(10px);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}
.nav-container {
max-width: 1200px;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: space-between;
}
.logo {
display: flex;
align-items: center;
gap: 0.5rem;
text-decoration: none;
}
.logo-text {
font-size: 1.5rem;
font-weight: 700;
background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.nav-links {
display: flex;
align-items: center;
gap: 2rem;
}
.nav-links a {
color: var(--color-text-muted);
font-weight: 500;
font-size: 0.95rem;
transition: color 0.2s ease;
}
.nav-links a:hover {
color: var(--color-text-high);
}
@media (max-width: 640px) {
.navbar {
padding: 1rem;
}
.nav-links {
gap: 1rem;
}
}
</style>

View File

@@ -0,0 +1,156 @@
<script lang="ts">
interface Props {
title: string;
description: string;
image_url: string;
download_url: string;
code_url: string;
}
let { title, description, image_url, download_url, code_url }: Props = $props();
let imageError = $state(false);
function handleImageError() {
imageError = true;
}
</script>
<div class="card">
<div class="card-image">
{#if !imageError && image_url !== '#'}
<img src={image_url} alt={title} onerror={handleImageError} loading="lazy" />
{:else}
<div class="card-image-placeholder">
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909M3.75 21h16.5A2.25 2.25 0 0022.5 18.75V5.25A2.25 2.25 0 0020.25 3H3.75A2.25 2.25 0 001.5 5.25v13.5A2.25 2.25 0 003.75 21z" />
</svg>
</div>
{/if}
</div>
<div class="card-body">
<h3>{title}</h3>
<p>{description}</p>
<div class="card-actions">
{#if code_url !== '#'}
<a href={code_url} target="_blank" rel="noopener noreferrer" class="btn btn-ghost">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M16 18l6-6-6-6M8 6l-6 6 6 6" />
</svg>
Code
</a>
{/if}
{#if download_url !== '#'}
<a href={download_url} target="_blank" rel="noopener noreferrer" class="btn btn-outline">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3" />
</svg>
Download
</a>
{/if}
</div>
</div>
</div>
<style>
.card {
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-card);
overflow: hidden;
transition: all 0.3s ease;
display: flex;
flex-direction: column;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 12px 24px -8px rgba(0, 0, 0, 0.3);
border-color: rgba(136, 136, 255, 0.3);
}
.card-image {
position: relative;
height: 180px;
background: linear-gradient(135deg, #1a2744, #0f172a);
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.card-image img {
width: 100%;
height: 100%;
object-fit: contain;
padding: 1.5rem;
}
.card-image-placeholder {
color: var(--color-text-muted);
opacity: 0.5;
}
.card-body {
padding: 1.5rem;
display: flex;
flex-direction: column;
flex: 1;
}
.card-body h3 {
font-size: 1.125rem;
font-weight: 600;
margin-bottom: 0.5rem;
color: var(--color-text-high);
}
.card-body p {
font-size: 0.9rem;
color: var(--color-text-muted);
line-height: 1.6;
flex: 1;
margin-bottom: 1.25rem;
}
.card-actions {
display: flex;
gap: 0.75rem;
flex-wrap: wrap;
}
.btn {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
border-radius: var(--radius-button);
font-size: 0.875rem;
font-weight: 500;
transition: all 0.2s ease;
text-decoration: none;
}
.btn-ghost {
background: transparent;
color: var(--color-text-muted);
border: 1px solid var(--color-border);
}
.btn-ghost:hover {
color: var(--color-text-high);
border-color: var(--color-primary);
background: rgba(136, 136, 255, 0.1);
}
.btn-outline {
background: transparent;
color: var(--color-primary);
border: 1px solid var(--color-primary);
}
.btn-outline:hover {
background: var(--color-primary);
color: white;
}
</style>

View File

@@ -0,0 +1,163 @@
<script lang="ts">
import ProjectCard from "./ProjectCard.svelte";
interface Project {
id: number;
title: string;
description: string;
image_url: string;
download_url: string;
code_url: string;
}
let projects = $state<Project[]>([]);
let loading = $state(true);
let error = $state("");
async function loadProjects() {
try {
loading = true;
error = "";
const response = await fetch("/projects.json");
if (!response.ok) throw new Error("Failed to load projects");
projects = await response.json();
} catch (e) {
error = "Could not load projects. Please try again later.";
} finally {
loading = false;
}
}
$effect(() => {
loadProjects();
});
</script>
<section id="projects" class="projects-section">
<div class="container">
<div class="section-header">
<h2>My Projects</h2>
<p>
A collection of open source tools, applications, and libraries
</p>
</div>
{#if loading}
<div class="loading">
<div class="spinner"></div>
<p>Loading projects...</p>
</div>
{:else if error}
<div class="error-state">
<p>{error}</p>
<button onclick={loadProjects} class="btn btn-primary"
>Retry</button
>
</div>
{:else}
<div class="projects-grid">
{#each projects as project (project.id)}
<ProjectCard
title={project.title}
description={project.description}
image_url={project.image_url}
download_url={project.download_url}
code_url={project.code_url}
/>
{/each}
</div>
{/if}
</div>
</section>
<style>
.projects-section {
padding: 5rem 2rem;
}
.section-header {
text-align: center;
margin-bottom: 3rem;
}
.section-header h2 {
margin-bottom: 0.75rem;
}
.section-header p {
color: var(--color-text-muted);
font-size: 1.1rem;
}
.projects-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 1.5rem;
}
.loading {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
padding: 3rem;
color: var(--color-text-muted);
}
.spinner {
width: 40px;
height: 40px;
border: 3px solid var(--color-border);
border-top-color: var(--color-primary);
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.error-state {
text-align: center;
padding: 3rem;
color: var(--color-text-muted);
}
.error-state p {
margin-bottom: 1rem;
}
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.75rem 1.5rem;
border-radius: var(--radius-button);
font-weight: 600;
font-size: 0.9rem;
transition: all 0.2s ease;
cursor: pointer;
border: none;
}
.btn-primary {
background: var(--color-primary);
color: white;
}
.btn-primary:hover {
background: #9d9dff;
}
@media (max-width: 640px) {
.projects-section {
padding: 3rem 1.5rem;
}
.projects-grid {
grid-template-columns: 1fr;
}
}
</style>

1
src/lib/index.ts Normal file
View File

@@ -0,0 +1 @@
// place files you want to import through the `$lib` alias in this folder.

12
src/routes/+layout.svelte Normal file
View File

@@ -0,0 +1,12 @@
<script lang="ts">
import '../app.css';
import favicon from '$lib/assets/favicon.svg';
let { children } = $props();
</script>
<svelte:head>
<link rel="icon" href={favicon} />
</svelte:head>
{@render children()}

13
src/routes/+page.svelte Normal file
View File

@@ -0,0 +1,13 @@
<script lang="ts">
import Navbar from '$lib/components/Navbar.svelte';
import Hero from '$lib/components/Hero.svelte';
import ProjectsSection from '$lib/components/ProjectsSection.svelte';
import Footer from '$lib/components/Footer.svelte';
</script>
<Navbar />
<main>
<Hero />
<ProjectsSection />
</main>
<Footer />

66
static/projects.json Normal file
View File

@@ -0,0 +1,66 @@
[
{
"id": 0,
"title": "Maltemedia",
"description": "A modern, fast, and secure application to stay updated with the latest news from different worlds.",
"image_url": "https://gitea.malxte.de/Bytemalte/marstemedia/raw/branch/main/public/logo.png",
"download_url": "https://gitea.malxte.de/Bytemalte/marstemedia/releases/tag/v0.4.2",
"code_url": "https://gitea.malxte.de/Bytemalte/marstemedia"
},
{
"id": 1,
"title": "Easy-Nostr",
"description": "A Rust Crate that makes Nostr-SDK easy to use with preprogrammed functions to call.",
"image_url": "",
"download_url": "#",
"code_url": "https://gitea.malxte.de/Bytemalte/malxte_de"
},
{
"id": 2,
"title": "Malxte.de Personal Rust Website",
"description": "My personal website, fully programmed using Rust and Yew.",
"image_url": "",
"download_url": "#",
"code_url": "https://gitea.malxte.de/Bytemalte/malxte_de"
},
{
"id": 3,
"title": "Bytemalte.de Rust Community Website",
"description": "A community website programmed with Rust and Leptos.",
"image_url": "",
"download_url": "#",
"code_url": "https://gitea.malxte.de/Bytemalte/bytemalte_de"
},
{
"id": 4,
"title": "Bytemalte Logo",
"description": "The official Bytemalte logo project.",
"image_url": "",
"download_url": "#",
"code_url": "#"
},
{
"id": 5,
"title": "Mdo",
"description": "Mdo is a very simple ToDo App fully written in Rust using Dioxus.",
"image_url": "#",
"download_url": "#",
"code_url": "https://gitea.malxte.de/Bytemalte/mdo"
},
{
"id": 6,
"title": "Mcalc",
"description": "Mcalc is a very simple Calculator example written fully in Rust using Dioxus.",
"image_url": "#",
"download_url": "#",
"code_url": "https://gitea.malxte.de/Bytemalte/mcalc"
},
{
"id": 7,
"title": "Mflow",
"description": "Mflow is a more advanced ToDo, Project management App example fully written in Rust using Dioxus",
"image_url": "#",
"download_url": "#",
"code_url": "https://gitea.malxte.de/Bytemalte/mcalc"
}
]

3
static/robots.txt Normal file
View File

@@ -0,0 +1,3 @@
# allow crawling everything by default
User-agent: *
Disallow:

17
svelte.config.js Normal file
View File

@@ -0,0 +1,17 @@
import adapter from '@sveltejs/adapter-auto';
/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
// See https://svelte.dev/docs/kit/adapters for more information about adapters.
adapter: adapter()
},
vitePlugin: {
dynamicCompileOptions: ({ filename }) =>
filename.includes('node_modules') ? undefined : { runes: true }
}
};
export default config;

20
tsconfig.json Normal file
View File

@@ -0,0 +1,20 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"rewriteRelativeImportExtensions": true,
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler"
}
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
//
// To make changes to top-level options such as include and exclude, we recommend extending
// the generated config; see https://svelte.dev/docs/kit/configuration#typescript
}

6
vite.config.ts Normal file
View File

@@ -0,0 +1,6 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sveltekit()]
});