Initial Apposaurus website

This commit is contained in:
2026-04-20 05:05:14 +00:00
parent 1c7f223652
commit 76516c3535
12 changed files with 757 additions and 14 deletions
+19
View File
@@ -0,0 +1,19 @@
---
import type { Project } from '../data/projects';
interface Props {
project: Project;
}
const { project } = Astro.props;
---
<article class="card project-card">
<div class="project-meta">{project.kind}</div>
<h3>{project.name}</h3>
<p>{project.short}</p>
<div class="tag-list">
{project.highlights.map((item) => <span class="tag">{item}</span>)}
</div>
<a class="project-link" href={`/project/${project.slug}`}>View project →</a>
</article>
+9
View File
@@ -0,0 +1,9 @@
export const legalNotes = {
disclaimer:
'This website structure is a technical draft. Before public launch, all legal texts should be completed with real company details and reviewed for legal accuracy.',
privacyCommon: [
'Server logs may include IP address, request time, requested URL, browser information, referrer and status codes.',
'This data may be processed to deliver the service securely, protect against abuse and keep the website or app stable.',
'No optional third-party tracking, contact forms, analytics or advertising details are included here unless they are explicitly added later.',
],
};
+66
View File
@@ -0,0 +1,66 @@
export type Project = {
slug: string;
name: string;
kind: 'Website' | 'App';
short: string;
description: string;
audience: string;
highlights: string[];
status: string;
url?: string;
privacySummary: string;
legalSummary: string;
};
export const projects: Project[] = [
{
slug: 'unverpackt-finder',
name: 'unverpackt-finder.de',
kind: 'Website',
short: 'A website that helps people discover zero-waste and package-free stores more easily.',
description:
'unverpackt-finder.de is built to make sustainable shopping options easier to discover. The project focuses on clear navigation, straightforward information and a search-driven experience with minimal friction.',
audience: 'People looking for local zero-waste or package-free stores and sustainable shopping alternatives.',
highlights: ['local discovery', 'clear search intent', 'simple content structure', 'SEO-friendly pages'],
status: 'Active project',
url: 'https://unverpackt-finder.de',
privacySummary:
'This project may process technically required server data such as IP address, request details, browser data and timestamps to deliver the website securely and reliably.',
legalSummary:
'This website is operated under the Apposaurus brand. Contact and provider details should be listed with the final business information before public launch.',
},
{
slug: 'seeds-for-minecraft-pe-java',
name: 'Seeds For Minecraft PE & Java',
kind: 'App',
short: 'An app for discovering interesting Minecraft seeds for Pocket Edition and Java.',
description:
'Seeds For Minecraft PE & Java is designed to present useful world seeds in a fast and easy-to-browse format. The focus is on quick discovery, practical browsing and a straightforward mobile-friendly product experience.',
audience: 'Minecraft players who want to find useful or interesting seeds without wasting time searching through noisy sources.',
highlights: ['mobile-first browsing', 'seed discovery', 'simple UX', 'focused content presentation'],
status: 'Active project',
privacySummary:
'This app may process technical usage data needed to provide the app, maintain security and ensure stable performance. If analytics, advertising or account features are added later, this policy must be expanded accordingly.',
legalSummary:
'This app is presented under Apposaurus. Final operator details, contact information and mandatory disclosures should be completed before publication in stores or on the web.',
},
{
slug: 'thanks-gratitude-mood-journal-app',
name: 'Thanks. Gratitude Mood Journal App',
kind: 'App',
short: 'A gratitude and mood journal app for simple daily reflection.',
description:
'Thanks is a calm journaling product focused on gratitude, reflection and mood awareness. It is meant to feel approachable, gentle and easy to use on a daily basis without visual clutter or complexity.',
audience: 'People who want a simple way to track gratitude, record mood and build a small daily reflection habit.',
highlights: ['daily reflection', 'mood tracking', 'simple journaling', 'calm product experience'],
status: 'Active project',
privacySummary:
'This app may process data that is necessary to store entries, secure the service and keep the app working correctly. If cloud sync, sign-in, notifications or third-party services are added later, the policy should be updated.',
legalSummary:
'This app is part of the Apposaurus portfolio. The final legal notice should include the full operator identity, address, contact details and any additional mandatory commercial disclosures.',
},
];
export function getProject(slug: string) {
return projects.find((project) => project.slug === slug);
}
+19
View File
@@ -0,0 +1,19 @@
export const company = {
name: 'Apposaurus',
founder: 'Justin Michael Eckenweber',
city: 'Essen',
country: 'Germany',
tagline: 'A simple home for products, apps and websites built with clarity and purpose.',
email: 'hey@unverpackt-finder.de',
phone: '028160031948',
addressLine1: 'Rühlestraße 5',
addressLine2: '45147 Essen',
addressLine3: 'Germany',
};
export const seo = {
title: 'Apposaurus — Apps and websites by a developer from Essen',
description:
'Apposaurus is the home for lightweight, SEO-friendly websites and useful apps. Built in Essen, focused on clarity, speed and good product presentation.',
url: 'https://apposaurus.de',
};
+58
View File
@@ -0,0 +1,58 @@
---
import '../styles/global.css';
import { company, seo } from '../data/site';
interface Props {
title?: string;
description?: string;
}
const { title = seo.title, description = seo.description } = Astro.props;
const canonical = new URL(Astro.url.pathname, seo.url).toString();
---
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="generator" content={Astro.generator} />
<meta name="description" content={description} />
<link rel="canonical" href={canonical} />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="icon" href="/favicon.ico" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content={company.name} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:url" content={canonical} />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<title>{title}</title>
</head>
<body>
<header class="site-header">
<div class="container nav">
<a class="brand" href="/">Apposaurus</a>
<nav class="nav-links" aria-label="Main navigation">
<a href="/#projects">Projects</a>
<a href="/#about">About</a>
</nav>
</div>
</header>
<slot />
<footer class="footer">
<div class="container footer-inner">
<div class="footer-copy">
<strong>{company.name}</strong> — apps and websites by a developer from {company.city}.
</div>
<div class="footer-links">
<a href="/">Home</a>
<a href="/#projects">Projects</a>
<a href="/privacy-policy">Privacy Policy</a>
<a href="/legal-notice">Legal Notice</a>
</div>
</div>
</footer>
</body>
</html>
+88 -14
View File
@@ -1,17 +1,91 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
import { company } from '../data/site';
import { projects } from '../data/projects';
---
---
<BaseLayout>
<main>
<section class="hero">
<div class="container hero-grid">
<div>
<span class="eyebrow">Developer from {company.city}</span>
<h1>A simple home for apps, websites and future products.</h1>
<p>
Apposaurus is the umbrella brand for my digital projects. It is meant to feel light, clear and easy to
browse — a place where each product gets a simple presentation and its own legal pages.
</p>
<div class="button-row">
<a class="button primary" href="#projects">See projects</a>
<a class="button secondary" href="#about">About Apposaurus</a>
</div>
<div class="stats">
<span><strong>{projects.length}</strong> projects</span>
<span><strong>{company.city}</strong>, Germany</span>
<span><strong>lightweight</strong> and SEO-friendly</span>
</div>
</div>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>Astro</title>
</head>
<body>
<h1>Astro</h1>
</body>
</html>
<div class="hero-note simple-panel">
<h2>What this site is for</h2>
<p>
Apposaurus brings together websites and apps in one consistent place. It stays intentionally simple,
keeps the focus on the products themselves and avoids over-designed layouts.
</p>
<p>
Every project can have its own detail page, privacy policy and legal notice under a clean
<code>/project/...</code> structure.
</p>
</div>
</div>
</section>
<section id="projects" class="section">
<div class="container">
<span class="eyebrow">Projects</span>
<h2>Current apps and websites</h2>
<div class="projects-list">
{projects.map((project) => (
<article class="project-row">
<div class="project-kind">{project.kind}</div>
<div>
<h3>{project.name}</h3>
<p>{project.short}</p>
<div class="tag-list">
{project.highlights.map((item) => <span class="tag">{item}</span>)}
</div>
</div>
<div class="project-actions">
<a class="project-link" href={`/project/${project.slug}`}>Open</a>
{project.url && <a class="project-link" href={project.url} target="_blank" rel="noreferrer">Visit</a>}
</div>
</article>
))}
</div>
</div>
</section>
<section id="about" class="section">
<div class="container two-col">
<div>
<span class="eyebrow">About</span>
<h2>Built to feel straightforward and welcoming</h2>
<p>
The goal is not to make Apposaurus look like a developer portfolio full of boxes and effects. It should
feel more like a calm product website: clear structure, readable copy and enough space for the projects
themselves.
</p>
</div>
<div class="simple-panel">
<h2>What can be added later</h2>
<ul class="simple-list">
<li>more products and launch pages</li>
<li>store links and download links</li>
<li>company information and contact details</li>
<li>partnerships, references or case studies</li>
</ul>
</div>
</div>
</section>
</main>
</BaseLayout>
+41
View File
@@ -0,0 +1,41 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
import { company } from '../data/site';
---
<BaseLayout title={`Legal notice — ${company.name}`} description={`Legal notice for ${company.name}.`}>
<main class="section">
<div class="container" style="max-width: 860px;">
<span class="eyebrow">Legal notice</span>
<h1 style="font-size: clamp(2.2rem, 4vw, 3.8rem); margin-bottom: 16px;">Apposaurus</h1>
<p>
This legal notice applies to the Apposaurus website. Individual projects may provide additional project-level
legal notice pages under their own <code>/project/...</code> routes.
</p>
<div class="card">
<h2>Provider information</h2>
<p>
{company.founder}<br />
{company.addressLine1}<br />
{company.addressLine2}<br />
{company.addressLine3}
</p>
<p>
Email: {company.email}<br />
Phone: {company.phone}
</p>
</div>
<div class="card">
<h2>Responsible for content</h2>
<p>
{company.founder}<br />
{company.addressLine1}<br />
{company.addressLine2}<br />
{company.addressLine3}
</p>
</div>
</div>
</main>
</BaseLayout>
+41
View File
@@ -0,0 +1,41 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
import { company } from '../data/site';
import { legalNotes } from '../data/legal';
---
<BaseLayout title={`Privacy policy — ${company.name}`} description={`Privacy policy for ${company.name}.`}>
<main class="section">
<div class="container" style="max-width: 860px;">
<span class="eyebrow">Privacy policy</span>
<h1 style="font-size: clamp(2.2rem, 4vw, 3.8rem); margin-bottom: 16px;">Apposaurus</h1>
<p>
This privacy policy applies to the Apposaurus website. Individual projects may also have their own privacy
policy pages under their respective <code>/project/...</code> routes.
</p>
<div class="card">
<h2>General processing information</h2>
<ul class="simple-list">
{legalNotes.privacyCommon.map((item) => <li>{item}</li>)}
</ul>
</div>
<div class="card">
<h2>Controller</h2>
<p>
{company.founder}<br />
{company.addressLine1}<br />
{company.addressLine2}<br />
{company.addressLine3}<br />
Email: {company.email}<br />
Phone: {company.phone}
</p>
</div>
<div class="notice">
<p>{legalNotes.disclaimer}</p>
</div>
</div>
</main>
</BaseLayout>
+50
View File
@@ -0,0 +1,50 @@
---
import BaseLayout from '../../../layouts/BaseLayout.astro';
import { projects } from '../../../data/projects';
export function getStaticPaths() {
return projects.map((project) => ({
params: { slug: project.slug },
props: { project },
}));
}
const { project } = Astro.props;
---
<BaseLayout title={`${project.name} — Apposaurus`} description={project.short}>
<main class="section">
<div class="container detail-grid">
<article class="card">
<div class="project-meta">{project.kind}</div>
<h1 style="font-size: clamp(2.2rem, 4vw, 3.6rem); margin-bottom: 14px;">{project.name}</h1>
<p>{project.short}</p>
<p>{project.description}</p>
<h2>Who it is for</h2>
<p>{project.audience}</p>
<h2>Highlights</h2>
<div class="tag-list">
{project.highlights.map((item) => <span class="tag">{item}</span>)}
</div>
</article>
<aside class="card">
<h2>Project info</h2>
<ul class="simple-list">
<li><strong>Status:</strong> {project.status}</li>
<li><strong>Type:</strong> {project.kind}</li>
<li><strong>Presented by:</strong> Apposaurus</li>
</ul>
<div class="button-row">
<a class="button secondary" href={`/project/${project.slug}/privacy-policy`}>Privacy policy</a>
<a class="button secondary" href={`/project/${project.slug}/legal-notice`}>Legal notice</a>
{project.url && (
<a class="button primary" href={project.url} target="_blank" rel="noreferrer">Visit website</a>
)}
</div>
</aside>
</div>
</main>
</BaseLayout>
@@ -0,0 +1,41 @@
---
import BaseLayout from '../../../layouts/BaseLayout.astro';
import { projects } from '../../../data/projects';
import { company } from '../../../data/site';
import { legalNotes } from '../../../data/legal';
export function getStaticPaths() {
return projects.map((project) => ({
params: { slug: project.slug },
props: { project },
}));
}
const { project } = Astro.props;
---
<BaseLayout title={`${project.name} legal notice — Apposaurus`} description={`Legal notice for ${project.name}.`}>
<main class="section">
<div class="container" style="max-width: 900px;">
<article class="card">
<div class="project-meta">Legal notice</div>
<h1 style="font-size: clamp(2rem, 4vw, 3.4rem);">{project.name}</h1>
<p>{project.legalSummary}</p>
<h2>Provider information</h2>
<p>
{company.name}<br />
Responsible person: {company.founder}<br />
{company.addressLine1}<br />
{company.addressLine2}<br />
Email: {company.email}<br />
Phone: {company.phone}
</p>
<div class="notice">
<p>{legalNotes.disclaimer}</p>
</div>
</article>
</div>
</main>
</BaseLayout>
@@ -0,0 +1,47 @@
---
import BaseLayout from '../../../layouts/BaseLayout.astro';
import { projects } from '../../../data/projects';
import { company } from '../../../data/site';
import { legalNotes } from '../../../data/legal';
export function getStaticPaths() {
return projects.map((project) => ({
params: { slug: project.slug },
props: { project },
}));
}
const { project } = Astro.props;
---
<BaseLayout title={`${project.name} privacy policy — Apposaurus`} description={`Privacy policy for ${project.name}.`}>
<main class="section">
<div class="container" style="max-width: 900px;">
<article class="card">
<div class="project-meta">Privacy policy</div>
<h1 style="font-size: clamp(2rem, 4vw, 3.4rem);">{project.name}</h1>
<p>{project.privacySummary}</p>
<h2>General processing information</h2>
<ul class="simple-list">
{legalNotes.privacyCommon.map((item) => <li>{item}</li>)}
</ul>
<h2>Controller</h2>
<p>
{company.name}<br />
{company.addressLine1}<br />
{company.addressLine2}<br />
Email: {company.email}
</p>
<div class="notice">
<p>
This page is a structured draft. Before launch, it should be completed with the real processing details,
data categories, legal bases, storage periods and third-party services used by this specific project.
</p>
</div>
</article>
</div>
</main>
</BaseLayout>
+278
View File
@@ -0,0 +1,278 @@
body {
margin: 0;
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
color: #1f2937;
background: #ffffff;
}
* {
box-sizing: border-box;
}
a {
color: inherit;
text-decoration: none;
}
.container {
width: min(1020px, calc(100% - 32px));
margin: 0 auto;
}
.site-header {
background: rgba(255, 255, 255, 0.96);
border-bottom: 1px solid #eceff3;
position: sticky;
top: 0;
z-index: 20;
}
.nav {
min-height: 72px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
}
.brand {
font-size: 1.08rem;
font-weight: 700;
color: #111827;
}
.nav-links,
.footer-links {
display: flex;
flex-wrap: wrap;
gap: 18px;
color: #6b7280;
font-size: 0.95rem;
}
.nav-links a:hover,
.footer-links a:hover,
.project-link:hover {
color: #111827;
}
.hero {
padding: 88px 0 48px;
}
.hero h1 {
font-size: clamp(2.7rem, 5vw, 4.8rem);
line-height: 0.97;
letter-spacing: -0.05em;
color: #111827;
margin: 0 0 18px;
}
h2,
h3 {
color: #111827;
margin-top: 0;
}
h2 {
font-size: clamp(1.55rem, 3vw, 2.1rem);
margin-bottom: 12px;
}
h3 {
font-size: 1.12rem;
margin-bottom: 8px;
}
p,
li {
line-height: 1.7;
color: #4b5563;
}
.section {
padding: 26px 0 54px;
}
.eyebrow {
display: inline-block;
margin-bottom: 14px;
color: #2563eb;
font-size: 0.92rem;
font-weight: 600;
}
.muted {
color: #6b7280;
}
.button-row {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin-top: 22px;
}
.button {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 46px;
padding: 0 18px;
border-radius: 999px;
font-weight: 600;
border: 1px solid transparent;
}
.button.primary {
background: #111827;
color: white;
}
.button.secondary {
background: #fff;
color: #111827;
border-color: #d1d5db;
}
.hero-grid,
.two-col,
.detail-grid {
display: grid;
grid-template-columns: 1.3fr 0.85fr;
gap: 36px;
}
.hero-note {
padding-top: 10px;
}
.simple-panel {
border-top: 1px solid #eceff3;
padding-top: 18px;
}
.stats {
display: flex;
flex-wrap: wrap;
gap: 18px;
margin-top: 28px;
color: #6b7280;
}
.stats strong {
color: #111827;
}
.projects-list {
border-top: 1px solid #eceff3;
}
.project-row {
display: grid;
grid-template-columns: 160px 1fr auto;
gap: 18px;
padding: 22px 0;
border-bottom: 1px solid #eceff3;
align-items: start;
}
.project-kind {
color: #2563eb;
font-size: 0.92rem;
font-weight: 600;
padding-top: 4px;
}
.project-row h3 {
margin-bottom: 6px;
}
.project-actions {
display: flex;
gap: 12px;
align-items: center;
justify-content: flex-end;
white-space: nowrap;
}
.project-link {
font-weight: 600;
color: #111827;
}
.tag-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 10px;
}
.tag {
background: #f6f7f9;
color: #4b5563;
border-radius: 999px;
padding: 7px 11px;
font-size: 0.84rem;
}
.simple-list {
display: grid;
gap: 10px;
padding-left: 18px;
}
.card {
border-top: 1px solid #eceff3;
padding-top: 18px;
}
.notice {
border-left: 3px solid #d1d5db;
padding-left: 16px;
margin-top: 18px;
}
.footer {
border-top: 1px solid #eceff3;
background: #fff;
margin-top: 56px;
}
.footer-inner {
padding: 26px 0 38px;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 16px;
}
.footer-copy {
color: #6b7280;
font-size: 0.95rem;
}
code {
background: #f3f4f6;
padding: 2px 6px;
border-radius: 6px;
color: #374151;
}
@media (max-width: 900px) {
.hero-grid,
.two-col,
.detail-grid,
.project-row {
grid-template-columns: 1fr;
}
.hero {
padding-top: 56px;
}
.project-actions {
justify-content: flex-start;
}
}