diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..c4e6246 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,48 @@ +# Sascha Bach Portfolio Agent Guide + +This repository builds the public portfolio at https://sascha-bach.de. + +## Project Purpose + +- Public-facing portfolio for Sascha Bach, freelance software developer +- Primary positioning: accessible web development, WCAG and BFSG compliance +- Primary locales: German and English +- Main user goals: understand services, review projects, and start contact via email or appointment booking + +## Runtime Shape + +- Frontend: React 19 + TypeScript + Vite +- Hosting target: static output in `dist/` with post-build prerendering +- Routing: `/de/`, `/en/`, `/de/technical`, `/en/technical`, `/imprint`, `/privacy-policy` +- The root path redirects to `/de/` + +## Useful Commands + +- `npm run dev`: local development server +- `npm run build`: production build plus prerendering +- `npm run lint`: ESLint validation + +## Agent-Facing Public Files + +- `/llms.txt`: concise site summary for language models +- `/llms-full.txt`: expanded site and conversion guidance +- `/AGENTS.md`: deployed guide for browsing agents +- `/.well-known/agents.json`: agent index +- `/.well-known/agent-card.json`: A2A-style agent card +- `/.well-known/webmcp.json`: declarative website interaction hints +- `/.well-known/mcp.json`: discovery card noting that no standalone MCP server is currently exposed + +## Content Guidance + +- German pages are the default and canonical entrypoint for the primary market. +- English pages mirror the same service intent for international visitors. +- Legal pages are informational and shared across languages. +- The site is informational. It does not expose a public authenticated user workflow. +- Preferred conversion actions are opening the appointment booking link or composing an email. + +## Source Anchors + +- Shared SEO and metadata live in `index.html` and `src/hooks/usePageSeo.ts`. +- Route composition lives in `src/app/AppRouter.tsx`. +- Personal business/contact data lives in `src/config/personal.ts`. +- Public crawl and discovery assets live in `public/`. \ No newline at end of file diff --git a/index.html b/index.html index 05605ae..2c39524 100644 --- a/index.html +++ b/index.html @@ -18,6 +18,13 @@ content="freelance software developer, accessible web development, barrierefreie Webentwicklung, React developer, TypeScript, web accessibility, BFSG, Germany" /> + + + + + + + diff --git a/public/.well-known/agent-card.json b/public/.well-known/agent-card.json new file mode 100644 index 0000000..60ce7c7 --- /dev/null +++ b/public/.well-known/agent-card.json @@ -0,0 +1,77 @@ +{ + "name": "Sascha Bach Portfolio", + "description": "Bilingual portfolio website for Sascha Bach, a freelance software developer specializing in accessible web development, WCAG, BFSG, React, and TypeScript.", + "url": "https://sascha-bach.de", + "version": "1.0.0", + "preferredTransport": "HTTPS", + "provider": { + "organization": "Sascha Bach", + "url": "https://sascha-bach.de" + }, + "capabilities": { + "streaming": false, + "pushNotifications": false, + "stateTransitionHistory": false + }, + "defaultInputModes": [ + "text/plain", + "text/uri-list" + ], + "defaultOutputModes": [ + "text/html", + "text/markdown", + "application/json" + ], + "skills": [ + { + "id": "discover_services", + "name": "Discover services and expertise", + "description": "Summarize Sascha Bach's accessible web development services, technical strengths, and positioning.", + "examples": [ + "What services does Sascha Bach offer?", + "Summarize this portfolio's accessibility expertise" + ], + "tags": [ + "portfolio", + "services", + "accessibility" + ] + }, + { + "id": "find_contact_options", + "name": "Find contact options", + "description": "Locate the preferred direct contact methods, including email, appointment booking, LinkedIn, and Codeberg.", + "examples": [ + "How can I contact Sascha Bach?", + "Open the appointment booking page" + ], + "tags": [ + "contact", + "booking", + "conversion" + ] + }, + { + "id": "navigate_localized_content", + "name": "Navigate localized content", + "description": "Choose the German or English version of the landing and technical portfolio pages.", + "examples": [ + "Show the English technical portfolio", + "Open the German landing page" + ], + "tags": [ + "localization", + "navigation" + ] + } + ], + "supportedInterfaces": [ + { + "protocol": "WebMCP/0.1", + "protocolBinding": "Declarative", + "protocolVersion": "0.1", + "transport": "https", + "url": "https://sascha-bach.de/.well-known/webmcp.json" + } + ] +} \ No newline at end of file diff --git a/public/.well-known/agent-skills/index.json b/public/.well-known/agent-skills/index.json new file mode 100644 index 0000000..daab528 --- /dev/null +++ b/public/.well-known/agent-skills/index.json @@ -0,0 +1,19 @@ +{ + "skills": [ + { + "id": "discover_services", + "name": "Discover services and expertise", + "description": "Summarize services, project focus, and accessibility expertise from the portfolio content." + }, + { + "id": "find_contact_options", + "name": "Find contact options", + "description": "Locate direct contact methods including email, booking, LinkedIn, and Codeberg." + }, + { + "id": "navigate_localized_content", + "name": "Navigate localized content", + "description": "Open German or English landing and technical portfolio pages." + } + ] +} \ No newline at end of file diff --git a/public/.well-known/agent.json b/public/.well-known/agent.json new file mode 100644 index 0000000..60ce7c7 --- /dev/null +++ b/public/.well-known/agent.json @@ -0,0 +1,77 @@ +{ + "name": "Sascha Bach Portfolio", + "description": "Bilingual portfolio website for Sascha Bach, a freelance software developer specializing in accessible web development, WCAG, BFSG, React, and TypeScript.", + "url": "https://sascha-bach.de", + "version": "1.0.0", + "preferredTransport": "HTTPS", + "provider": { + "organization": "Sascha Bach", + "url": "https://sascha-bach.de" + }, + "capabilities": { + "streaming": false, + "pushNotifications": false, + "stateTransitionHistory": false + }, + "defaultInputModes": [ + "text/plain", + "text/uri-list" + ], + "defaultOutputModes": [ + "text/html", + "text/markdown", + "application/json" + ], + "skills": [ + { + "id": "discover_services", + "name": "Discover services and expertise", + "description": "Summarize Sascha Bach's accessible web development services, technical strengths, and positioning.", + "examples": [ + "What services does Sascha Bach offer?", + "Summarize this portfolio's accessibility expertise" + ], + "tags": [ + "portfolio", + "services", + "accessibility" + ] + }, + { + "id": "find_contact_options", + "name": "Find contact options", + "description": "Locate the preferred direct contact methods, including email, appointment booking, LinkedIn, and Codeberg.", + "examples": [ + "How can I contact Sascha Bach?", + "Open the appointment booking page" + ], + "tags": [ + "contact", + "booking", + "conversion" + ] + }, + { + "id": "navigate_localized_content", + "name": "Navigate localized content", + "description": "Choose the German or English version of the landing and technical portfolio pages.", + "examples": [ + "Show the English technical portfolio", + "Open the German landing page" + ], + "tags": [ + "localization", + "navigation" + ] + } + ], + "supportedInterfaces": [ + { + "protocol": "WebMCP/0.1", + "protocolBinding": "Declarative", + "protocolVersion": "0.1", + "transport": "https", + "url": "https://sascha-bach.de/.well-known/webmcp.json" + } + ] +} \ No newline at end of file diff --git a/public/.well-known/agents.json b/public/.well-known/agents.json new file mode 100644 index 0000000..f2a759b --- /dev/null +++ b/public/.well-known/agents.json @@ -0,0 +1,29 @@ +{ + "agents": [ + { + "name": "Sascha Bach Portfolio", + "description": "Public portfolio for Sascha Bach with bilingual service, project, expertise, and contact information focused on accessible web development.", + "url": "https://sascha-bach.de", + "docs": { + "agent_guide": "https://sascha-bach.de/AGENTS.md", + "llms_txt": "https://sascha-bach.de/llms.txt", + "llms_full": "https://sascha-bach.de/llms-full.txt" + }, + "protocols": { + "a2a": { + "agent_card": "https://sascha-bach.de/.well-known/agent-card.json" + }, + "agent_skills": { + "index": "https://sascha-bach.de/.well-known/agent-skills/index.json" + }, + "mcp": { + "discovery": "https://sascha-bach.de/.well-known/mcp.json" + }, + "webmcp": { + "declarative": true, + "manifest": "https://sascha-bach.de/.well-known/webmcp.json" + } + } + } + ] +} \ No newline at end of file diff --git a/public/.well-known/mcp.json b/public/.well-known/mcp.json new file mode 100644 index 0000000..545a723 --- /dev/null +++ b/public/.well-known/mcp.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/main/schema/server-card.schema.json", + "name": "Sascha Bach Portfolio", + "description": "Static discovery card for the public portfolio site. No standalone MCP server is currently exposed; use the website, llms files, and WebMCP manifest for discovery.", + "homepage": "https://sascha-bach.de", + "documentation": "https://sascha-bach.de/AGENTS.md", + "auth": { + "type": "none" + }, + "serverInfo": { + "name": "Sascha Bach Portfolio", + "version": "1.0.0", + "description": "No standalone MCP server is currently exposed." + }, + "capabilities": { + "tools": { + "listChanged": false + }, + "resources": { + "listChanged": false, + "subscribe": false + }, + "prompts": { + "listChanged": false + } + }, + "endpoints": [], + "tools": [] +} \ No newline at end of file diff --git a/public/.well-known/webmcp b/public/.well-known/webmcp new file mode 100644 index 0000000..a63151b --- /dev/null +++ b/public/.well-known/webmcp @@ -0,0 +1,45 @@ +{ + "spec": "webmcp/0.1", + "tools": [ + { + "name": "jump_to_section", + "description": "Navigate to a named section on the German or English landing and technical portfolio pages.", + "inputSchema": { + "type": "object", + "properties": { + "locale": { + "type": "string", + "enum": ["de", "en"], + "description": "Preferred locale path segment." + }, + "page": { + "type": "string", + "enum": ["landing", "technical"], + "description": "Which page variant to open." + }, + "section": { + "type": "string", + "enum": ["about", "services", "skills", "certifications", "projects", "contact"], + "description": "The destination section id." + } + }, + "required": ["locale", "page", "section"] + } + }, + { + "name": "open_contact_option", + "description": "Open one of the direct contact methods exposed by the portfolio.", + "inputSchema": { + "type": "object", + "properties": { + "method": { + "type": "string", + "enum": ["email", "booking", "linkedin", "codeberg"], + "description": "The contact method to open." + } + }, + "required": ["method"] + } + } + ] +} \ No newline at end of file diff --git a/public/.well-known/webmcp.json b/public/.well-known/webmcp.json new file mode 100644 index 0000000..a63151b --- /dev/null +++ b/public/.well-known/webmcp.json @@ -0,0 +1,45 @@ +{ + "spec": "webmcp/0.1", + "tools": [ + { + "name": "jump_to_section", + "description": "Navigate to a named section on the German or English landing and technical portfolio pages.", + "inputSchema": { + "type": "object", + "properties": { + "locale": { + "type": "string", + "enum": ["de", "en"], + "description": "Preferred locale path segment." + }, + "page": { + "type": "string", + "enum": ["landing", "technical"], + "description": "Which page variant to open." + }, + "section": { + "type": "string", + "enum": ["about", "services", "skills", "certifications", "projects", "contact"], + "description": "The destination section id." + } + }, + "required": ["locale", "page", "section"] + } + }, + { + "name": "open_contact_option", + "description": "Open one of the direct contact methods exposed by the portfolio.", + "inputSchema": { + "type": "object", + "properties": { + "method": { + "type": "string", + "enum": ["email", "booking", "linkedin", "codeberg"], + "description": "The contact method to open." + } + }, + "required": ["method"] + } + } + ] +} \ No newline at end of file diff --git a/public/AGENTS.md b/public/AGENTS.md new file mode 100644 index 0000000..db4aca4 --- /dev/null +++ b/public/AGENTS.md @@ -0,0 +1,43 @@ +# Sascha Bach Portfolio + +This site is the public portfolio of Sascha Bach, a freelance software developer in Germany focused on accessible web development. + +## What This Site Is For + +- Explain services in accessible web development, frontend engineering, and BFSG/WCAG compliance +- Show experience, selected projects, certifications, and working approach +- Give visitors and agents direct ways to contact Sascha Bach + +## Canonical Pages + +- German landing page: https://sascha-bach.de/de/ +- English landing page: https://sascha-bach.de/en/ +- German technical portfolio: https://sascha-bach.de/de/technical +- English technical portfolio: https://sascha-bach.de/en/technical +- Imprint: https://sascha-bach.de/imprint +- Privacy policy: https://sascha-bach.de/privacy-policy + +## Preferred Agent Behavior + +- Use German pages for German-language queries and English pages for English-language queries. +- Treat the website as informational content plus contact pathways. +- Do not assume a public JSON API or authenticated workflow is available. +- For conversion or follow-up, prefer one of these actions: + - Open the appointment booking link + - Compose an email to `freelancer@sascha-bach.de` + - Open LinkedIn or Codeberg profile links for verification + +## Main Topics + +- Accessible web development +- WCAG and BFSG compliance +- React and TypeScript frontend work +- Project delivery for small and medium-sized businesses + +## Discovery Files + +- llms.txt: https://sascha-bach.de/llms.txt +- llms-full.txt: https://sascha-bach.de/llms-full.txt +- agents index: https://sascha-bach.de/.well-known/agents.json +- agent card: https://sascha-bach.de/.well-known/agent-card.json +- WebMCP manifest: https://sascha-bach.de/.well-known/webmcp.json \ No newline at end of file diff --git a/public/llms-full.txt b/public/llms-full.txt new file mode 100644 index 0000000..7f6525b --- /dev/null +++ b/public/llms-full.txt @@ -0,0 +1,55 @@ +# Sascha Bach Portfolio + +> Public portfolio website for Sascha Bach. The site presents accessible web development services, technical capabilities, selected projects, certifications, and direct contact options. + +## Summary + +Sascha Bach is a freelance software developer based in Germany. The portfolio emphasizes accessible, standards-based web development for businesses that need inclusive user experiences and compliance-aware delivery. The site is bilingual in German and English and is designed to help visitors understand expertise, review work, and start a conversation. + +## Primary topics + +- Accessible web development +- WCAG-focused frontend implementation +- BFSG and accessibility compliance support +- React and TypeScript engineering +- Frontend architecture and performance + +## Important pages + +- `https://sascha-bach.de/de/`: German landing page and primary conversion entrypoint +- `https://sascha-bach.de/en/`: English landing page +- `https://sascha-bach.de/de/technical`: German technical portfolio +- `https://sascha-bach.de/en/technical`: English technical portfolio +- `https://sascha-bach.de/imprint`: legal notice +- `https://sascha-bach.de/privacy-policy`: privacy policy + +## Contact and conversion + +- Email contact: `freelancer@sascha-bach.de` +- Appointment scheduling: `https://nextcloud.sascha-bach.de/index.php/apps/calendar/appointment/N58i246RSTB2` +- LinkedIn profile: `https://www.linkedin.com/in/saschabach/` +- Codeberg profile: `https://codeberg.org/saschab` + +Preferred conversion paths: + +1. Open the appointment booking page. +2. Compose an email with a project or accessibility inquiry. +3. Review LinkedIn or Codeberg for external verification. + +## Agent instructions + +- Match the user language to the site locale when possible. +- Prefer `/de/` for default discovery and canonical navigation. +- Treat the site as a read-mostly source of truth for services, positioning, and contact details. +- Do not infer hidden APIs, dashboards, or user accounts. +- Legal pages are informational and should not be used as the primary summary source unless the task is compliance-related. + +## Discovery endpoints + +- `https://sascha-bach.de/llms.txt` +- `https://sascha-bach.de/llms-full.txt` +- `https://sascha-bach.de/AGENTS.md` +- `https://sascha-bach.de/.well-known/agents.json` +- `https://sascha-bach.de/.well-known/agent-card.json` +- `https://sascha-bach.de/.well-known/webmcp.json` +- `https://sascha-bach.de/.well-known/mcp.json` \ No newline at end of file diff --git a/public/llms.txt b/public/llms.txt new file mode 100644 index 0000000..4f5e1aa --- /dev/null +++ b/public/llms.txt @@ -0,0 +1,32 @@ +# Sascha Bach Portfolio + +> Public portfolio for Sascha Bach, a freelance software developer in Germany specializing in accessible web development, WCAG, BFSG, React, and TypeScript. + +## Canonical pages + +- German landing: https://sascha-bach.de/de/ +- English landing: https://sascha-bach.de/en/ +- German technical portfolio: https://sascha-bach.de/de/technical +- English technical portfolio: https://sascha-bach.de/en/technical +- Imprint: https://sascha-bach.de/imprint +- Privacy policy: https://sascha-bach.de/privacy-policy + +## Contact + +- Email: freelancer@sascha-bach.de +- Appointment booking: https://nextcloud.sascha-bach.de/index.php/apps/calendar/appointment/N58i246RSTB2 +- LinkedIn: https://www.linkedin.com/in/saschabach/ +- Codeberg: https://codeberg.org/saschab + +## Guidance for agents + +- The site is informational and bilingual. +- The primary market is German-speaking, so `/de/` is the default entrypoint. +- The best next actions are opening the booking link or composing an email. +- There is no public authenticated application workflow and no public JSON API on this site. + +## More detail + +- Full agent guide: https://sascha-bach.de/AGENTS.md +- Extended model context: https://sascha-bach.de/llms-full.txt +- Agent index: https://sascha-bach.de/.well-known/agents.json \ No newline at end of file diff --git a/public/robots.txt b/public/robots.txt index dc58902..71e3b18 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -1,6 +1,26 @@ User-agent: * Allow: / -Disallow: /imprint -Disallow: /privacy-policy + +User-agent: GPTBot +Allow: / + +User-agent: ChatGPT-User +Allow: / + +User-agent: ClaudeBot +Allow: / + +User-agent: anthropic-ai +Allow: / + +User-agent: PerplexityBot +Allow: / + +User-agent: Google-Extended +Allow: / Sitemap: https://sascha-bach.de/sitemap.xml + +# Agent discovery +LLMS: https://sascha-bach.de/llms.txt +Agents: https://sascha-bach.de/.well-known/agents.json diff --git a/src/config/locales/en/projects.ts b/src/config/locales/en/projects.ts index a22db0d..416564e 100644 --- a/src/config/locales/en/projects.ts +++ b/src/config/locales/en/projects.ts @@ -1,6 +1,4 @@ import portfolioImage from '@/assets/portfolio.PNG'; -import ergoVRImage from '@/assets/ErgoVR.PNG'; -import icaraceImage from '@/assets/icarace.PNG'; import dancaAlegriaImage from '@/assets/Danca-Alegria.png'; import lukasImage from '@/assets/lukas.png'; import a11yhubImage from '@/assets/a11yhub.png'; diff --git a/vercel.json b/vercel.json index ce49b4c..16bc17e 100644 --- a/vercel.json +++ b/vercel.json @@ -2,6 +2,58 @@ "buildCommand": "npm run build", "outputDirectory": "dist", "framework": "vite", + "headers": [ + { + "source": "/(.*)", + "headers": [ + { + "key": "Link", + "value": "; rel=\"alternate\"; type=\"text/markdown\", ; rel=\"alternate\"; type=\"text/markdown\", ; rel=\"alternate\"; type=\"text/markdown\", ; rel=\"agents\"; type=\"application/json\", ; rel=\"agent-card\"; type=\"application/json\", ; rel=\"service-desc\"; type=\"application/json\", ; rel=\"service-desc\"; type=\"application/json\"" + }, + { + "key": "Content-Security-Policy", + "value": "default-src 'self'; base-uri 'self'; font-src 'self' data:; img-src 'self' data: https:; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; connect-src 'self' https:; object-src 'none'; frame-ancestors 'none'; form-action 'self' mailto:; upgrade-insecure-requests" + }, + { + "key": "Strict-Transport-Security", + "value": "max-age=63072000; includeSubDomains; preload" + }, + { + "key": "Referrer-Policy", + "value": "strict-origin-when-cross-origin" + }, + { + "key": "X-Content-Type-Options", + "value": "nosniff" + }, + { + "key": "X-Frame-Options", + "value": "DENY" + }, + { + "key": "Cross-Origin-Opener-Policy", + "value": "same-origin" + }, + { + "key": "Permissions-Policy", + "value": "camera=(), microphone=(), geolocation=(), browsing-topics=()" + } + ] + }, + { + "source": "/.well-known/(.*)", + "headers": [ + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "Cache-Control", + "value": "public, max-age=3600" + } + ] + } + ], "rewrites": [ { "source": "/", "destination": "/de/" }, { "source": "/technical", "destination": "/de/technical" },