Tailwind v4 migration, massive SEO optimization, and fixes

- Migrate to Tailwind CSS v4 with @theme, @layer, @plugin syntax
- Upgrade to Next.js 16.1, React 19.2, TypeScript 5.9.3
- Add 8 technology landing pages (Next.js, React, TypeScript, Tailwind CSS, Claude AI, Python, n8n, KI-Agenten)
- Expand SEO keywords across all locales (de/en/sr) for home, layout, cities, services
- Add tech-specific keywords to all 22 city pages
- Add FAQ schema + seoKeywords to service detail pages
- Add CollectionPage JSON-LD to blog listing
- Fix city page: dynamic region, BASE_URL canonicals, x-default hreflang
- Rewrite terms page with full German AGB, English and Serbian translations
- Remove dead crypto import and unused CSRF middleware (Edge Runtime fix)
- Remove crawlDelay from robots.ts
- Update sitemap with technology pages (~198 indexed URLs)
- Remove deprecated tailwind.config.js

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-06 20:40:19 +01:00
co-authored by Claude Opus 4.6
parent 03d6228a68
commit 62ae5140b9
35 changed files with 3111 additions and 1311 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference path="./.next/types/routes.d.ts" />
import "./.next/types/routes.d.ts";
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
+1 -4
View File
@@ -11,10 +11,7 @@ const nextConfig: NextConfig = {
// Enable React strict mode for better error detection
reactStrictMode: true,
// Temporarily ignore ESLint and TypeScript during build (pre-existing issues)
eslint: {
ignoreDuringBuilds: true,
},
// Temporarily ignore TypeScript during build (pre-existing issues)
typescript: {
ignoreBuildErrors: true,
},
+13 -13
View File
@@ -23,7 +23,7 @@
"@mdx-js/mdx": "^3.1.0",
"@mdx-js/react": "^3.1.0",
"@next-safe/middleware": "^0.10.0",
"@next/mdx": "^15.1.0",
"@next/mdx": "^16.1.0",
"@supabase/ssr": "^0.8.0",
"@supabase/supabase-js": "^2.39.7",
"class-variance-authority": "^0.7.1",
@@ -31,13 +31,13 @@
"framer-motion": "^11.18.2",
"gray-matter": "^4.0.3",
"lucide-react": "^0.475.0",
"next": "^15.1.0",
"next-intl": "^3.26.0",
"next": "^16.1.0",
"next-intl": "^4.7.2",
"next-mdx-remote": "^5.0.0",
"openai": "^4.77.0",
"pg": "^8.13.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-ga4": "^2.1.0",
"react-intersection-observer": "^9.8.1",
"tailwind-merge": "^3.0.1",
@@ -46,24 +46,24 @@
},
"devDependencies": {
"@playwright/test": "^1.58.0",
"@tailwindcss/forms": "^0.5.7",
"@tailwindcss/forms": "^0.5.11",
"@tailwindcss/postcss": "^4.1.0",
"@testing-library/jest-dom": "^6.0.0",
"@testing-library/react": "^14.0.0",
"@testing-library/react": "^16.1.0",
"@types/mdx": "^2.0.13",
"@types/node": "^22.0.0",
"@types/pg": "^8.11.10",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@types/react": "^19.2.0",
"@types/react-dom": "^19.2.0",
"@vitest/coverage-v8": "1.6.1",
"autoprefixer": "^10.4.18",
"eslint": "^9.9.1",
"eslint-config-next": "^15.1.0",
"eslint-config-next": "^16.1.0",
"jsdom": "^23.2.0",
"playwright-lighthouse": "^4.0.0",
"postcss": "^8.4.35",
"sharp": "^0.34.3",
"tailwindcss": "^3.4.1",
"typescript": "^5.5.3",
"tailwindcss": "^4.1.0",
"typescript": "5.9.3",
"vitest": "^1.3.1"
}
}
+11 -19
View File
@@ -24,8 +24,8 @@ export default defineConfig({
// Retry on CI only
retries: process.env.CI ? 2 : 0,
// Opt out of parallel tests on CI
workers: process.env.CI ? 1 : undefined,
// Limit workers to avoid overwhelming the dev server
workers: process.env.CI ? 1 : 4,
// Reporter to use
reporter: [
@@ -35,8 +35,8 @@ export default defineConfig({
// Shared settings for all projects
use: {
// Base URL to use in actions like `await page.goto('/')`
baseURL: 'http://localhost:3000',
// Base URL - uses dedicated test port to avoid conflicts with other dev servers
baseURL: 'http://localhost:3100',
// Collect trace when retrying the failed test
trace: 'on-first-retry',
@@ -45,16 +45,14 @@ export default defineConfig({
screenshot: 'only-on-failure',
},
// Configure projects - Chromium only for Lighthouse compatibility
// Configure projects for E2E testing
// Note: --remote-debugging-port removed to allow parallel test execution.
// For Lighthouse performance tests, use a separate config with a single worker.
projects: [
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
// Launch options for Lighthouse
launchOptions: {
args: ['--remote-debugging-port=9222'],
},
},
},
// Mobile Chrome for responsive testing
@@ -62,9 +60,6 @@ export default defineConfig({
name: 'mobile-chrome',
use: {
...devices['Pixel 5'],
launchOptions: {
args: ['--remote-debugging-port=9223'],
},
},
},
// Tablet viewport for responsive testing
@@ -72,18 +67,15 @@ export default defineConfig({
name: 'tablet',
use: {
viewport: { width: 768, height: 1024 },
launchOptions: {
args: ['--remote-debugging-port=9224'],
},
},
},
],
// Configure web server to start before tests
// Configure web server to start before tests on a dedicated port
webServer: {
command: 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
command: 'pnpm dev --port 3100',
url: 'http://localhost:3100',
reuseExistingServer: false,
timeout: 120000,
},
+1529 -785
View File
File diff suppressed because it is too large Load Diff
+1 -2
View File
@@ -1,6 +1,5 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
'@tailwindcss/postcss': {},
},
};
+11
View File
@@ -6,6 +6,7 @@ import Image from 'next/image';
import type { Metadata } from 'next';
import { getAllBlogPosts, BlogPost } from '@/lib/blog';
import { legacyBlogPosts } from '@/data/legacyBlogPosts';
import { CollectionPageJsonLd } from '@/components/seo';
const POSTS_PER_PAGE = 12;
@@ -370,8 +371,18 @@ export default async function BlogPage({ params, searchParams }: Props) {
const endIndex = startIndex + POSTS_PER_PAGE;
const posts = allPosts.slice(startIndex, endIndex);
// Map posts for CollectionPage schema
const collectionItems = allPosts.slice(0, 20).map((post) => ({
name: post.title,
description: post.excerpt,
url: `/${locale}/blog/${post.slug}`,
dateCreated: post.date,
technologies: post.tags,
}));
return (
<main className="min-h-screen">
<CollectionPageJsonLd locale={locale as 'de' | 'en' | 'sr'} projects={collectionItems} />
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16 sm:py-20 lg:py-24">
{/* Header Section */}
<div className="mb-12 sm:mb-16 lg:mb-20">
+11 -5
View File
@@ -3,7 +3,7 @@ import { NextIntlClientProvider } from 'next-intl';
import { getMessages, setRequestLocale } from 'next-intl/server';
import { Inter } from 'next/font/google';
import { locales, localeMetadata, seoKeywords, type Locale } from '@/i18n/config';
import type { Metadata } from 'next';
import type { Metadata, Viewport } from 'next';
import { Header, Footer, GlobalBackground } from '@/components/layout';
import { PersonJsonLd, ProfessionalServiceJsonLd, OrganizationJsonLd, WebSiteJsonLd } from '@/components/seo';
import { WebVitals } from '@/components/analytics';
@@ -21,6 +21,12 @@ type Props = {
params: Promise<{ locale: string }>;
};
export const viewport: Viewport = {
width: 'device-width',
initialScale: 1,
viewportFit: 'cover',
};
export function generateStaticParams() {
return locales.map((locale) => ({ locale }));
}
@@ -39,9 +45,9 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
};
const descriptions: Record<Locale, string> = {
de: 'Fullstack Entwicklung für Websites, Apps und SaaS mit Next.js, React und TypeScript.',
en: 'Fullstack development for websites, apps and SaaS with Next.js, React and TypeScript.',
sr: 'Fullstack razvoj za web stranice, aplikacije i SaaS sa Next.js, React i TypeScript.',
de: 'Fullstack Developer & KI Entwickler Websites, Apps und SaaS mit Next.js, React, TypeScript und Claude AI. Künstliche Intelligenz & AI Agents aus Köln.',
en: 'Fullstack Developer & AI Expert Websites, apps and SaaS with Next.js, React, TypeScript and Claude AI. Artificial intelligence & AI agent development.',
sr: 'Fullstack Developer & AI Expert Web stranice, aplikacije i SaaS sa Next.js, React, TypeScript i Claude AI. Veštačka inteligencija i AI agenti.',
};
// Generate alternates using localeMetadata
@@ -121,7 +127,7 @@ export default async function LocaleLayout({ children, params }: Props) {
<div className="min-h-screen flex flex-col relative">
<GlobalBackground />
<Header />
<main className="flex-1 pt-16 relative z-10">{children}</main>
<main className="flex-1 pt-14 sm:pt-16 relative z-10">{children}</main>
<Footer />
</div>
{/* AI Chatbot - lazy loaded for performance */}
+85 -6
View File
@@ -4,7 +4,7 @@ import Link from 'next/link';
import { ArrowLeft, ArrowRight, CheckCircle, Brain, Mic, Workflow, Cloud, Code, Bot, Building, ShoppingCart, Factory } from 'lucide-react';
import { notFound } from 'next/navigation';
import { allServices, getServiceBySlug, getAllServiceSlugs } from '@/data/services';
import { BreadcrumbJsonLd, ServiceJsonLd } from '@/components/seo';
import { BreadcrumbJsonLd, ServiceJsonLd, FAQJsonLd } from '@/components/seo';
import { type Locale, locales } from '@/i18n/config';
type Props = {
@@ -51,15 +51,17 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
const localePath = locale === 'de' ? `/leistungen/${slug}` : `/${locale}/leistungen/${slug}`;
// Enhanced keywords for English
const enhancedKeywords = locale === 'en'
? [...service.technologies, 'Remote AI Developer', 'Hire AI Developer', 'AI Consultant USA', 'AI Developer UK'].join(', ')
: service.technologies.join(', ');
// Use seoKeywords if available, otherwise fall back to technologies
const seoKw = service.seoKeywords?.[locale as Locale] || [];
const allKeywords = [...service.technologies, ...seoKw];
if (locale === 'en') {
allKeywords.push('Remote AI Developer', 'Hire AI Developer', 'AI Consultant USA', 'AI Developer UK');
}
return {
title,
description: service.description[locale as Locale] || service.description.de,
keywords: enhancedKeywords,
keywords: allKeywords.join(', '),
alternates: {
canonical: `${BASE_URL}${localePath}`,
languages: {
@@ -76,6 +78,14 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
type: 'website',
locale: locale === 'de' ? 'de_DE' : locale === 'sr' ? 'sr_RS' : 'en_US',
alternateLocale: ['de_DE', 'en_US', 'sr_RS'].filter(l => l !== (locale === 'de' ? 'de_DE' : locale === 'sr' ? 'sr_RS' : 'en_US')),
images: [
{
url: `${BASE_URL}/images/og-image.avif`,
width: 1200,
height: 630,
alt: `${service.name[locale as Locale]} - Damjan Savić`,
},
],
},
};
}
@@ -101,6 +111,48 @@ export default async function ServicePage({ params }: Props) {
// Get related services (excluding current)
const relatedServices = allServices.filter((s) => s.slug !== slug).slice(0, 3);
// FAQ items for this service
const serviceFaqItems = locale === 'de' ? [
{
question: `Was kostet ${service.name.de}?`,
answer: `Die Kosten für ${service.name.de} variieren je nach Projektumfang und Komplexität. Ich erstelle gerne ein individuelles Angebot nach einem kostenlosen Erstgespräch.`,
},
{
question: `Welche Technologien nutzen Sie für ${service.name.de}?`,
answer: `Für ${service.name.de} setze ich auf bewährte Technologien: ${service.technologies.join(', ')}. Die genaue Auswahl hängt von Ihren Anforderungen ab.`,
},
{
question: `Wie lange dauert ein typisches ${service.name.de}-Projekt?`,
answer: `Die Dauer eines ${service.name.de}-Projekts hängt vom Umfang ab. Einfache Projekte können in 2-4 Wochen abgeschlossen werden, komplexere Lösungen in 6-12 Wochen.`,
},
] : locale === 'en' ? [
{
question: `How much does ${service.name.en} cost?`,
answer: `The cost of ${service.name.en} varies depending on project scope and complexity. I'm happy to provide a custom quote after a free initial consultation.`,
},
{
question: `What technologies do you use for ${service.name.en}?`,
answer: `For ${service.name.en}, I use proven technologies: ${service.technologies.join(', ')}. The exact selection depends on your requirements.`,
},
{
question: `How long does a typical ${service.name.en} project take?`,
answer: `The duration of a ${service.name.en} project depends on scope. Simple projects can be completed in 2-4 weeks, more complex solutions in 6-12 weeks.`,
},
] : [
{
question: `Koliko košta ${service.name.sr}?`,
answer: `Troškovi za ${service.name.sr} variraju u zavisnosti od obima i kompleksnosti projekta. Rado ću vam dati individualizovanu ponudu nakon besplatnog inicijalnog razgovora.`,
},
{
question: `Koje tehnologije koristite za ${service.name.sr}?`,
answer: `Za ${service.name.sr} koristim proverene tehnologije: ${service.technologies.join(', ')}. Tačan izbor zavisi od vaših zahteva.`,
},
{
question: `Koliko traje tipičan ${service.name.sr} projekat?`,
answer: `Trajanje ${service.name.sr} projekta zavisi od obima. Jednostavni projekti mogu se završiti za 2-4 nedelje, kompleksnija rešenja za 6-12 nedelja.`,
},
];
return (
<div className="min-h-screen">
<BreadcrumbJsonLd items={breadcrumbItems} locale={locale as Locale} />
@@ -110,6 +162,7 @@ export default async function ServicePage({ params }: Props) {
url={`/${locale}/leistungen/${slug}`}
locale={locale as Locale}
/>
<FAQJsonLd items={serviceFaqItems} locale={locale as Locale} />
{/* Hero Section */}
<section className="py-24">
@@ -226,6 +279,32 @@ export default async function ServicePage({ params }: Props) {
</div>
</section>
{/* FAQ Section */}
<section className="py-16">
<div className="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
<h2 className="text-2xl font-bold text-white text-center mb-8">
{locale === 'de'
? `Häufige Fragen zu ${service.name.de}`
: locale === 'sr'
? `Česta pitanja o ${service.name.sr}`
: `FAQ about ${service.name.en}`}
</h2>
<div className="space-y-4">
{serviceFaqItems.map((item, index) => (
<div
key={index}
className="bg-zinc-800/30 border border-zinc-800 rounded-lg p-6"
>
<h3 className="text-lg font-semibold text-white mb-2">
{item.question}
</h3>
<p className="text-zinc-400">{item.answer}</p>
</div>
))}
</div>
</div>
</section>
{/* CTA Section */}
<section className="py-24">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
+24
View File
@@ -6,6 +6,7 @@ import Image from 'next/image';
import { Brain, Mic, Workflow, Cloud, Code, ArrowRight, MapPin, Bot, Building, ShoppingCart, Factory } from 'lucide-react';
import { services, techServices, industryServices } from '@/data/services';
import { cities } from '@/data/cities';
import { technologies } from '@/data/technologies';
import { BreadcrumbJsonLd, FAQJsonLd } from '@/components/seo';
import { type Locale } from '@/i18n/config';
@@ -393,6 +394,29 @@ export default async function ServicesPage({ params }: Props) {
</div>
</section>
{/* Technologies Section */}
<section className="pb-24">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<h2 className="text-2xl font-bold text-white mb-8">
{locale === 'de' ? 'Technologien' : locale === 'sr' ? 'Tehnologije' : 'Technologies'}
</h2>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{technologies.map((tech) => (
<Link
key={tech.slug}
href={`/${locale}/leistungen/technologie/${tech.slug}`}
className="group flex items-center gap-3 p-4 bg-zinc-800/30 border border-zinc-800 rounded-lg hover:border-zinc-700 transition-all duration-300"
>
<Code className="h-5 w-5 text-zinc-500 group-hover:text-white transition-colors" />
<span className="text-zinc-300 group-hover:text-white transition-colors font-medium">
{tech.name}
</span>
</Link>
))}
</div>
</div>
</section>
{/* Cities Section */}
<section className="py-24 bg-zinc-800/20">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
@@ -12,6 +12,8 @@ type Props = {
params: Promise<{ locale: string; city: string }>;
};
const BASE_URL = process.env.NEXT_PUBLIC_SITE_URL || 'https://damjan-savic.com';
const iconMap: Record<string, React.ComponentType<{ className?: string }>> = {
Brain,
Mic,
@@ -39,15 +41,15 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
}
const titles: Record<string, string> = {
de: `KI Entwickler ${city.name.de} - AI & Automatisierung | Damjan Savić`,
en: `AI Developer ${city.name.en} - AI & Automation | Damjan Savić`,
sr: `AI Developer ${city.name.sr} - AI & Automatizacija | Damjan Savić`,
de: `Fullstack Developer & KI Entwickler ${city.name.de} | Claude AI, Next.js, React | Damjan Savić`,
en: `AI & Fullstack Developer ${city.name.en} | Claude AI, Next.js, React | Damjan Savić`,
sr: `AI & Fullstack Developer ${city.name.sr} | Claude AI, Next.js, React | Damjan Savić`,
};
const descriptions: Record<string, string> = {
de: `Professionelle KI-Entwicklung und Automatisierung in ${city.name.de}. Voice AI, Prozessautomatisierung, SaaS-Entwicklung. Ihr AI & Automation Specialist für ${city.name.de} und ${city.region}.`,
en: `Professional AI development and automation in ${city.name.en}. Voice AI, process automation, SaaS development. Your AI & Automation Specialist for ${city.name.en} and ${city.region}.`,
sr: `Profesionalni AI razvoj i automatizacija u ${city.name.sr}. Voice AI, automatizacija procesa, SaaS razvoj. Vaš AI & Automation Specialist za ${city.name.sr} i ${city.region}.`,
de: `Fullstack Developer & KI Entwickler in ${city.name.de}. Claude AI, Next.js, React, TypeScript, Tailwind CSS. Voice AI, Prozessautomatisierung, SaaS-Entwicklung. Ihr AI & Automation Specialist für ${city.name.de} und ${city.region}.`,
en: `Fullstack Developer & AI Expert in ${city.name.en}. Claude AI, Next.js, React, TypeScript, Tailwind CSS. Voice AI, process automation, SaaS development. Your AI & Automation Specialist for ${city.name.en} and ${city.region}.`,
sr: `Fullstack Developer & AI Expert u ${city.name.sr}. Claude AI, Next.js, React, TypeScript, Tailwind CSS. Voice AI, automatizacija procesa, SaaS razvoj. Vaš AI & Automation Specialist za ${city.name.sr} i ${city.region}.`,
};
return {
@@ -55,17 +57,27 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
description: descriptions[locale] || descriptions.de,
keywords: city.keywords[locale as Locale]?.join(', ') || city.keywords.de.join(', '),
alternates: {
canonical: `/${locale}/leistungen/standort/${citySlug}`,
canonical: `${BASE_URL}/${locale}/leistungen/standort/${citySlug}`,
languages: {
de: `/de/leistungen/standort/${citySlug}`,
en: `/en/leistungen/standort/${citySlug}`,
sr: `/sr/leistungen/standort/${citySlug}`,
'x-default': `${BASE_URL}/de/leistungen/standort/${citySlug}`,
de: `${BASE_URL}/de/leistungen/standort/${citySlug}`,
en: `${BASE_URL}/en/leistungen/standort/${citySlug}`,
sr: `${BASE_URL}/sr/leistungen/standort/${citySlug}`,
},
},
openGraph: {
title: titles[locale] || titles.de,
description: descriptions[locale] || descriptions.de,
type: 'website',
url: `${BASE_URL}/${locale}/leistungen/standort/${citySlug}`,
images: [
{
url: `${BASE_URL}/images/og-image.avif`,
width: 1200,
height: 630,
alt: `Damjan Savić - AI Developer ${city.name.en}`,
},
],
},
};
}
@@ -91,6 +103,14 @@ export default async function CityPage({ params }: Props) {
question: `Bieten Sie Ihre Dienstleistungen auch vor Ort in ${city.name.de} an?`,
answer: `Ja, ich arbeite mit Unternehmen in ${city.name.de} und Umgebung. Die meisten Projekte wickle ich remote ab, aber persönliche Meetings in ${city.name.de} sind bei Bedarf möglich.`,
},
{
question: `Entwickeln Sie mit Next.js und React in ${city.name.de}?`,
answer: `Ja, ich bin auf moderne Webentwicklung mit Next.js, React, TypeScript und Tailwind CSS spezialisiert. Für Unternehmen in ${city.name.de} entwickle ich performante, SEO-optimierte Web-Anwendungen und SaaS-Produkte mit diesem Tech-Stack.`,
},
{
question: `Setzen Sie Claude AI für Projekte in ${city.name.de} ein?`,
answer: `Ja, ich integriere Claude AI von Anthropic sowie andere KI-Modelle wie GPT-4 in Geschäftsprozesse. Ob KI-Agenten, Chatbots oder automatisierte Workflows ich setze modernste Künstliche Intelligenz für Unternehmen in ${city.name.de} ein.`,
},
{
question: `Welche Branchen bedienen Sie in ${city.name.de}?`,
answer: `Ich arbeite branchenübergreifend mit Startups, mittelständischen Unternehmen und Konzernen in ${city.name.de}. Besonders häufig sind Projekte in den Bereichen E-Commerce, Fintech, Healthcare und B2B-Services.`,
@@ -104,6 +124,14 @@ export default async function CityPage({ params }: Props) {
question: `Do you also offer your services on-site in ${city.name.en}?`,
answer: `Yes, I work with companies in ${city.name.en} and the surrounding area. Most projects are handled remotely, but in-person meetings in ${city.name.en} are possible if needed.`,
},
{
question: `Do you develop with Next.js and React in ${city.name.en}?`,
answer: `Yes, I specialize in modern web development with Next.js, React, TypeScript, and Tailwind CSS. For companies in ${city.name.en}, I build performant, SEO-optimized web applications and SaaS products using this tech stack.`,
},
{
question: `Do you use Claude AI for projects in ${city.name.en}?`,
answer: `Yes, I integrate Claude AI by Anthropic as well as other AI models like GPT-4 into business processes. Whether AI agents, chatbots, or automated workflows I leverage cutting-edge artificial intelligence for companies in ${city.name.en}.`,
},
{
question: `Which industries do you serve in ${city.name.en}?`,
answer: `I work across industries with startups, medium-sized companies and corporations in ${city.name.en}. Projects are particularly common in e-commerce, fintech, healthcare and B2B services.`,
@@ -117,6 +145,14 @@ export default async function CityPage({ params }: Props) {
question: `Da li nudite usluge i na licu mesta u ${city.name.sr}?`,
answer: `Da, radim sa kompanijama u ${city.name.sr} i okolini. Većina projekata se obrađuje remote, ali lični sastanci u ${city.name.sr} su mogući po potrebi.`,
},
{
question: `Da li razvijate sa Next.js i React u ${city.name.sr}?`,
answer: `Da, specijalizovan sam za moderni web razvoj sa Next.js, React, TypeScript i Tailwind CSS. Za kompanije u ${city.name.sr} pravim performantne, SEO-optimizovane web aplikacije i SaaS proizvode sa ovim tech stack-om.`,
},
{
question: `Da li koristite Claude AI za projekte u ${city.name.sr}?`,
answer: `Da, integrisem Claude AI od Anthropic-a kao i druge AI modele poput GPT-4 u poslovne procese. Bilo da su u pitanju AI agenti, chatbotovi ili automatizovani radni tokovi koristim najsavremeniju veštačku inteligenciju za kompanije u ${city.name.sr}.`,
},
{
question: `Koje industrije opslužujete u ${city.name.sr}?`,
answer: `Radim u svim industrijama sa startapima, srednjim preduzećima i korporacijama u ${city.name.sr}. Projekti su posebno česti u e-commerce, fintech, zdravstvu i B2B uslugama.`,
@@ -162,7 +198,7 @@ export default async function CityPage({ params }: Props) {
<div className="flex items-center gap-2 text-zinc-400 mb-4">
<MapPin className="h-5 w-5" />
<span>{city.region}, Deutschland</span>
<span>{city.region}</span>
</div>
<h1 className="text-4xl md:text-5xl font-bold text-white mb-6">
@@ -175,7 +211,7 @@ export default async function CityPage({ params }: Props) {
{/* Keywords as tags */}
<div className="flex flex-wrap gap-2">
{city.keywords[locale as Locale]?.slice(0, 5).map((keyword) => (
{city.keywords[locale as Locale]?.map((keyword) => (
<span
key={keyword}
className="px-3 py-1 bg-zinc-800/50 border border-zinc-700 text-sm text-zinc-300 rounded-full"
@@ -0,0 +1,362 @@
import { setRequestLocale } from 'next-intl/server';
import type { Metadata } from 'next';
import Link from 'next/link';
import { ArrowLeft, ArrowRight, CheckCircle, Code, Zap, MapPin } from 'lucide-react';
import { notFound } from 'next/navigation';
import { technologies, getTechnologyBySlug, getAllTechnologySlugs } from '@/data/technologies';
import { allServices, getServiceBySlug } from '@/data/services';
import { cities } from '@/data/cities';
import { BreadcrumbJsonLd, ServiceJsonLd, FAQJsonLd } from '@/components/seo';
import { type Locale, locales } from '@/i18n/config';
type Props = {
params: Promise<{ locale: string; tech: string }>;
};
const BASE_URL = process.env.NEXT_PUBLIC_SITE_URL || 'https://damjan-savic.com';
export async function generateStaticParams() {
const slugs = getAllTechnologySlugs();
return locales.flatMap((locale) =>
slugs.map((tech) => ({
locale,
tech,
}))
);
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { locale, tech: techSlug } = await params;
const tech = getTechnologyBySlug(techSlug);
if (!tech) {
return { title: 'Technology Not Found' };
}
const title = `${tech.title[locale as Locale]} | Damjan Savić`;
return {
title,
description: tech.description[locale as Locale],
keywords: tech.keywords[locale as Locale].join(', '),
alternates: {
canonical: `${BASE_URL}/${locale}/leistungen/technologie/${techSlug}`,
languages: {
'x-default': `${BASE_URL}/de/leistungen/technologie/${techSlug}`,
de: `${BASE_URL}/de/leistungen/technologie/${techSlug}`,
en: `${BASE_URL}/en/leistungen/technologie/${techSlug}`,
sr: `${BASE_URL}/sr/leistungen/technologie/${techSlug}`,
},
},
openGraph: {
title,
description: tech.description[locale as Locale],
url: `${BASE_URL}/${locale}/leistungen/technologie/${techSlug}`,
type: 'website',
locale: locale === 'de' ? 'de_DE' : locale === 'sr' ? 'sr_RS' : 'en_US',
alternateLocale: ['de_DE', 'en_US', 'sr_RS'].filter(l => l !== (locale === 'de' ? 'de_DE' : locale === 'sr' ? 'sr_RS' : 'en_US')),
images: [
{
url: `${BASE_URL}/images/og-image.avif`,
width: 1200,
height: 630,
alt: `${tech.name} Developer - Damjan Savić`,
},
],
},
};
}
export default async function TechnologyPage({ params }: Props) {
const { locale, tech: techSlug } = await params;
setRequestLocale(locale);
const tech = getTechnologyBySlug(techSlug);
if (!tech) {
notFound();
}
const breadcrumbItems = [
{ name: locale === 'de' ? 'Start' : locale === 'sr' ? 'Početna' : 'Home', url: `/${locale}` },
{ name: locale === 'de' ? 'Leistungen' : locale === 'sr' ? 'Usluge' : 'Services', url: `/${locale}/leistungen` },
{ name: tech.name, url: `/${locale}/leistungen/technologie/${techSlug}` },
];
const faqItems = locale === 'de' ? [
{
question: `Warum ${tech.name} für mein Projekt?`,
answer: `${tech.name} bietet hervorragende Performance, Skalierbarkeit und Entwicklerproduktivität. Als erfahrener ${tech.name} Entwickler kann ich Ihnen die beste Lösung für Ihre Anforderungen empfehlen.`,
},
{
question: `Was kostet ein ${tech.name}-Projekt?`,
answer: `Die Kosten hängen vom Projektumfang ab. Ich erstelle gerne ein individuelles Angebot nach einem kostenlosen Erstgespräch. Einfache Projekte starten ab ca. 2.000€.`,
},
{
question: `Wie lange dauert ein ${tech.name}-Projekt?`,
answer: `Je nach Komplexität zwischen 2 und 12 Wochen. Ein MVP kann in 4-6 Wochen realisiert werden. Ich arbeite agil mit wöchentlichen Updates.`,
},
] : locale === 'en' ? [
{
question: `Why choose ${tech.name} for my project?`,
answer: `${tech.name} offers excellent performance, scalability and developer productivity. As an experienced ${tech.name} developer, I can recommend the best solution for your requirements.`,
},
{
question: `How much does a ${tech.name} project cost?`,
answer: `The cost depends on the project scope. I'm happy to provide a custom quote after a free initial consultation. Simple projects start from around $2,200.`,
},
{
question: `How long does a ${tech.name} project take?`,
answer: `Depending on complexity, between 2 and 12 weeks. An MVP can be realized in 4-6 weeks. I work agile with weekly updates.`,
},
] : [
{
question: `Zašto odabrati ${tech.name} za moj projekat?`,
answer: `${tech.name} nudi odlične performanse, skalabilnost i produktivnost. Kao iskusni ${tech.name} developer, mogu vam preporučiti najbolje rešenje za vaše zahteve.`,
},
{
question: `Koliko košta ${tech.name} projekat?`,
answer: `Troškovi zavise od obima projekta. Rado ću vam dati individualizovanu ponudu nakon besplatnog inicijalnog razgovora. Jednostavni projekti počinju od oko 2.000€.`,
},
{
question: `Koliko traje ${tech.name} projekat?`,
answer: `U zavisnosti od kompleksnosti, između 2 i 12 nedelja. MVP se može realizovati za 4-6 nedelja. Radim agilno sa nedeljnim ažuriranjima.`,
},
];
// Related services
const relatedServiceData = tech.relatedServices
.map((slug) => getServiceBySlug(slug))
.filter(Boolean);
// Other technologies for cross-linking
const otherTechnologies = technologies.filter((t) => t.slug !== techSlug).slice(0, 4);
// Select cities for cross-linking (mix of regions)
const featuredCities = cities.filter((c) =>
['koeln', 'berlin', 'muenchen', 'london', 'new-york', 'beograd'].includes(c.slug)
);
return (
<div className="min-h-screen">
<BreadcrumbJsonLd items={breadcrumbItems} locale={locale as Locale} />
<ServiceJsonLd
name={`${tech.name} ${locale === 'de' ? 'Entwicklung' : locale === 'sr' ? 'Razvoj' : 'Development'}`}
description={tech.description[locale as Locale]}
url={`/${locale}/leistungen/technologie/${techSlug}`}
locale={locale as Locale}
/>
<FAQJsonLd items={faqItems} locale={locale as Locale} />
{/* Hero Section */}
<section className="py-24">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<Link
href={`/${locale}/leistungen`}
className="inline-flex items-center gap-2 text-zinc-400 hover:text-white transition-colors mb-8"
>
<ArrowLeft className="h-4 w-4" />
{locale === 'de' ? 'Alle Leistungen' : locale === 'sr' ? 'Sve usluge' : 'All Services'}
</Link>
<div className="flex items-center gap-4 mb-6">
<div className="p-4 bg-zinc-800/50 rounded-xl">
<Code className="h-8 w-8 text-white" />
</div>
</div>
<h1 className="text-4xl md:text-5xl font-bold text-white mb-6">
{tech.title[locale as Locale]}
</h1>
<p className="text-xl text-zinc-400 mb-8">
{tech.description[locale as Locale]}
</p>
{/* Keywords as tags */}
<div className="flex flex-wrap gap-2">
{tech.keywords[locale as Locale].map((keyword) => (
<span
key={keyword}
className="px-3 py-1 bg-zinc-800/50 border border-zinc-700 text-sm text-zinc-300 rounded-full"
>
{keyword}
</span>
))}
</div>
</div>
</section>
{/* Features Section */}
<section className="py-16 bg-zinc-800/20">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<h2 className="text-2xl font-bold text-white mb-8">
{locale === 'de' ? `${tech.name} Was ich anbiete` : locale === 'sr' ? `${tech.name} Šta nudim` : `${tech.name} What I offer`}
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{tech.features[locale as Locale].map((feature, index) => (
<div
key={index}
className="flex items-start gap-3 p-4 bg-zinc-800/30 rounded-lg"
>
<CheckCircle className="h-5 w-5 text-green-500 flex-shrink-0 mt-0.5" />
<span className="text-zinc-300">{feature}</span>
</div>
))}
</div>
</div>
</section>
{/* Use Cases Section */}
<section className="py-16">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<h2 className="text-2xl font-bold text-white mb-8">
{locale === 'de' ? 'Anwendungsbeispiele' : locale === 'sr' ? 'Primeri primene' : 'Use Cases'}
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{tech.useCases[locale as Locale].map((useCase, index) => (
<div
key={index}
className="p-6 bg-zinc-800/30 border border-zinc-800 rounded-xl"
>
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-zinc-700 rounded-full flex items-center justify-center text-sm font-semibold text-white">
{index + 1}
</div>
<span className="text-zinc-200">{useCase}</span>
</div>
</div>
))}
</div>
</div>
</section>
{/* Related Services */}
{relatedServiceData.length > 0 && (
<section className="py-16 bg-zinc-800/20">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<h2 className="text-2xl font-bold text-white mb-8">
{locale === 'de' ? 'Passende Leistungen' : locale === 'sr' ? 'Odgovarajuće usluge' : 'Related Services'}
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{relatedServiceData.map((service) => service && (
<Link
key={service.slug}
href={`/${locale}/leistungen/${service.slug}`}
className="group flex items-start gap-4 p-6 bg-zinc-800/30 border border-zinc-800 rounded-xl hover:border-zinc-700 transition-all duration-300"
>
<div className="p-2 bg-zinc-800/50 rounded-lg">
<Zap className="h-5 w-5 text-zinc-400 group-hover:text-white transition-colors" />
</div>
<div>
<h3 className="text-lg font-semibold text-white mb-1">
{service.name[locale as Locale]}
</h3>
<p className="text-sm text-zinc-400">
{service.shortDescription[locale as Locale]}
</p>
</div>
</Link>
))}
</div>
</div>
</section>
)}
{/* Other Technologies */}
<section className="py-16">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<h2 className="text-2xl font-bold text-white mb-8">
{locale === 'de' ? 'Weitere Technologien' : locale === 'sr' ? 'Ostale Tehnologije' : 'Other Technologies'}
</h2>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{otherTechnologies.map((otherTech) => (
<Link
key={otherTech.slug}
href={`/${locale}/leistungen/technologie/${otherTech.slug}`}
className="group flex items-center gap-2 p-4 bg-zinc-800/30 border border-zinc-800 rounded-lg hover:border-zinc-700 transition-all duration-300"
>
<Code className="h-4 w-4 text-zinc-500 group-hover:text-white transition-colors" />
<span className="text-zinc-300 group-hover:text-white transition-colors">
{otherTech.name}
</span>
</Link>
))}
</div>
</div>
</section>
{/* Cities Cross-Link */}
<section className="py-16 bg-zinc-800/20">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<h2 className="text-2xl font-bold text-white mb-8">
{locale === 'de' ? `${tech.name} Entwickler in Ihrer Stadt` : locale === 'sr' ? `${tech.name} Developer u vašem gradu` : `${tech.name} Developer in your city`}
</h2>
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
{featuredCities.map((city) => (
<Link
key={city.slug}
href={`/${locale}/leistungen/standort/${city.slug}`}
className="group flex items-center gap-2 p-4 bg-zinc-800/30 border border-zinc-800 rounded-lg hover:border-zinc-700 transition-all duration-300"
>
<MapPin className="h-4 w-4 text-zinc-500 group-hover:text-white transition-colors" />
<span className="text-zinc-300 group-hover:text-white transition-colors">
{city.name[locale as Locale]}
</span>
</Link>
))}
</div>
</div>
</section>
{/* FAQ Section */}
<section className="py-16">
<div className="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
<h2 className="text-2xl font-bold text-white text-center mb-8">
{locale === 'de' ? `Häufige Fragen zu ${tech.name}` : locale === 'sr' ? `Česta pitanja o ${tech.name}` : `FAQ about ${tech.name}`}
</h2>
<div className="space-y-4">
{faqItems.map((item, index) => (
<div
key={index}
className="bg-zinc-800/30 border border-zinc-800 rounded-lg p-6"
>
<h3 className="text-lg font-semibold text-white mb-2">
{item.question}
</h3>
<p className="text-zinc-400">{item.answer}</p>
</div>
))}
</div>
</div>
</section>
{/* CTA Section */}
<section className="py-24">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
<h2 className="text-3xl font-bold text-white mb-4">
{locale === 'de'
? `${tech.name}-Projekt starten?`
: locale === 'sr'
? `Započnite ${tech.name} projekat?`
: `Start a ${tech.name} project?`}
</h2>
<p className="text-zinc-400 mb-8">
{locale === 'de'
? 'Lassen Sie uns besprechen, wie ich Ihnen helfen kann.'
: locale === 'sr'
? 'Razgovarajmo o tome kako vam mogu pomoći.'
: "Let's discuss how I can help you."}
</p>
<Link
href={`/${locale}/contact`}
className="inline-flex items-center px-8 py-4 bg-white text-zinc-900 font-semibold rounded-lg hover:bg-zinc-100 transition-colors"
>
{locale === 'de' ? 'Kontakt aufnehmen' : locale === 'sr' ? 'Kontaktirajte me' : 'Get in touch'}
<ArrowRight className="h-5 w-5 ml-2" />
</Link>
</div>
</section>
</div>
);
}
+33 -18
View File
@@ -12,38 +12,53 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { locale } = await params;
const titles: Record<string, string> = {
de: 'KI Entwickler Köln | AI Agents & Automatisierung | Damjan Savić',
en: 'AI Developer Cologne | AI Agents & Automation | Damjan Savić',
sr: 'AI Developer Keln | AI Agenti & Automatizacija | Damjan Savić',
de: 'Fullstack Developer & KI Entwickler Köln | Claude AI, Next.js, React | Damjan Savić',
en: 'AI & Fullstack Developer | Claude AI, Next.js, React, TypeScript | Damjan Savić',
sr: 'AI & Fullstack Developer | Claude AI, Next.js, React | Damjan Savić',
};
const descriptions: Record<string, string> = {
de: 'Freelance KI Entwickler aus Köln. Spezialisiert auf KI-Agenten, Voice AI, Prozessautomatisierung mit n8n und SaaS-Entwicklung. Über 7 Jahre Erfahrung.',
en: 'Remote AI Developer & Automation Expert based in Germany. Building AI agents, Voice AI systems, and n8n automations for clients in USA, UK, and Europe. 7+ years experience. Available for international projects.',
sr: 'AI Developer i Automation Specialist iz Nemačke, poreklo iz Srbije. Specijalizovan za AI agente, Voice AI, n8n automatizaciju i SaaS razvoj. Radim sa klijentima iz Srbije, Nemačke i dijaspore. 7+ godina iskustva.',
de: 'Freelance Fullstack Developer & KI Entwickler aus Köln. Spezialisiert auf Claude AI, KI-Agenten, Next.js, React, TypeScript und Tailwind CSS. Prozessautomatisierung mit n8n, Voice AI und SaaS-Entwicklung. Über 7 Jahre Erfahrung in Künstlicher Intelligenz und Webentwicklung.',
en: 'Remote Fullstack Developer & AI Expert based in Germany. Building Claude AI integrations, AI agents, and modern web apps with Next.js, React, TypeScript and Tailwind CSS. Voice AI, n8n automation, and SaaS development for clients in USA, UK, and Europe. 7+ years experience.',
sr: 'AI & Fullstack Developer iz Nemačke, poreklo iz Srbije. Specijalizovan za Claude AI, AI agente, Next.js, React, TypeScript i Tailwind CSS. Voice AI, n8n automatizacija i SaaS razvoj za klijente iz Srbije, Nemačke i dijaspore. 7+ godina iskustva u veštačkoj inteligenciji.',
};
const keywords: Record<string, string[]> = {
de: [
'Fullstack Developer Köln',
'KI Entwickler Köln',
'Claude AI Entwickler',
'Next.js Entwickler',
'React Entwickler',
'TypeScript Entwickler',
'Tailwind CSS Entwickler',
'Künstliche Intelligenz',
'AI Developer Deutschland',
'KI Entwickler DACH',
'AI Agentur Köln',
'KI-Agenten Entwicklung',
'Prozessautomatisierung',
'n8n Entwickler',
'Voice AI',
'KI-Agenten',
'Fullstack Entwickler',
'SaaS Entwicklung',
'Automatisierung',
'GPT-4 Integration',
'Claude AI Integration',
'KI Entwickler Wien',
'AI Developer Zürich',
'Python Entwickler',
'Freelancer Deutschland',
'Claude AI Integration',
],
en: [
'Fullstack Developer',
'AI Developer',
'Claude AI Developer',
'Claude AI Integration',
'Next.js Developer',
'React Developer',
'TypeScript Developer',
'Tailwind CSS Developer',
'Artificial Intelligence',
'AI Agent Developer',
'Remote AI Developer',
'AI Developer USA',
'AI Developer UK',
@@ -51,32 +66,32 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
'Process Automation Expert',
'n8n Developer',
'Voice AI Developer',
'AI Agents Developer',
'Fullstack Developer',
'SaaS Development',
'GPT-4 Integration',
'ChatGPT Integration',
'Python Developer',
'Remote Developer',
'Freelance AI Developer',
'AI Consultant',
'Automation Specialist',
],
sr: [
'AI Developer Srbija',
'Fullstack Programer Srbija',
'Claude AI Developer',
'Next.js Programer',
'React Programer',
'TypeScript Programer',
'Veštačka Inteligencija',
'AI Programer Beograd',
'AI Developer Novi Sad',
'Fullstack Programer Srbija',
'AI Agenti',
'Automatizacija procesa',
'n8n Automatizacija',
'Voice AI Srbija',
'AI Agenti',
'SaaS Razvoj',
'GPT-4 Integracija',
'Python Programer Srbija',
'React Programer Beograd',
'Freelance Developer Srbija',
'Remote Programer Nemačka',
'Srpski Developer Dijaspora',
'Web Razvoj Beograd',
],
};
+277 -48
View File
@@ -1,6 +1,5 @@
import { setRequestLocale } from 'next-intl/server';
import type { Metadata } from 'next';
import { getTranslations } from 'next-intl/server';
import Link from 'next/link';
type Props = {
@@ -11,13 +10,24 @@ const BASE_URL = process.env.NEXT_PUBLIC_SITE_URL || 'https://damjan-savic.com';
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: 'pages.terms.seo' });
const titles: Record<string, string> = {
de: 'Allgemeine Geschäftsbedingungen | Damjan Savić - KI Entwickler Köln',
en: 'Terms and Conditions | Damjan Savić - AI Developer',
sr: 'Uslovi korišćenja | Damjan Savić - AI Developer',
};
const descriptions: Record<string, string> = {
de: 'Allgemeine Geschäftsbedingungen (AGB) für die Dienstleistungen und Portfolio-Website von Damjan Savić, Fullstack Developer & KI Entwickler aus Köln.',
en: 'Terms and Conditions for the services and portfolio website of Damjan Savić, Fullstack Developer & AI Developer.',
sr: 'Uslovi korišćenja za usluge i portfolio veb sajt Damjana Savića, Fullstack Developer & AI Developer.',
};
const localePath = locale === 'de' ? '/terms' : `/${locale}/terms`;
return {
title: t('title'),
description: t('description'),
title: titles[locale] || titles.de,
description: descriptions[locale] || descriptions.de,
alternates: {
canonical: `${BASE_URL}${localePath}`,
languages: {
@@ -28,8 +38,8 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
},
},
openGraph: {
title: t('title'),
description: t('description'),
title: titles[locale] || titles.de,
description: descriptions[locale] || descriptions.de,
url: `${BASE_URL}${localePath}`,
type: 'website',
locale: locale === 'de' ? 'de_DE' : locale === 'sr' ? 'sr_RS' : 'en_US',
@@ -42,64 +52,283 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
};
}
type TermsContent = {
title: string;
lastUpdated: string;
sections: { title: string; content: string; items?: string[] }[];
contact: { title: string; content: string[] };
privacyNote: { text: string; link: string };
};
export default async function TermsPage({ params }: Props) {
const { locale } = await params;
setRequestLocale(locale);
const t = await getTranslations('pages.terms');
const content: Record<string, TermsContent> = {
de: {
title: 'Allgemeine Geschäftsbedingungen',
lastUpdated: 'Zuletzt aktualisiert: Februar 2026',
sections: [
{
title: '1. Geltungsbereich',
content: 'Diese Allgemeinen Geschäftsbedingungen (AGB) gelten für alle Dienstleistungen und Angebote von Damjan Savić im Bereich Fullstack-Entwicklung, KI-Entwicklung, Prozessautomatisierung und IT-Beratung sowie für die Nutzung der Website damjan-savic.com.',
},
{
title: '2. Leistungsumfang',
content: 'Der Umfang der zu erbringenden Leistungen ergibt sich aus der jeweiligen Projektvereinbarung oder dem Angebot. Leistungen umfassen unter anderem:',
items: [
'Entwicklung von Webanwendungen und SaaS-Lösungen (Next.js, React, TypeScript)',
'KI-Entwicklung und Integration (Claude AI, GPT, Voice AI)',
'Prozessautomatisierung (n8n, Make, individuelle Lösungen)',
'IT-Beratung und technische Konzeptentwicklung',
'Wartung und Support bestehender Anwendungen',
],
},
{
title: '3. Angebote und Vertragsschluss',
content: 'Angebote sind freibleibend und unverbindlich. Ein Vertrag kommt erst durch schriftliche Bestätigung oder durch Aufnahme der Leistungserbringung zustande. Mündliche Nebenabreden bedürfen der schriftlichen Bestätigung.',
},
{
title: '4. Vergütung und Zahlungsbedingungen',
content: 'Die Vergütung richtet sich nach der jeweiligen Vereinbarung. Rechnungen sind innerhalb von 14 Tagen nach Rechnungsstellung ohne Abzug zahlbar. Bei Zahlungsverzug werden Verzugszinsen in gesetzlicher Höhe berechnet. Bei größeren Projekten können Abschlagszahlungen vereinbart werden.',
},
{
title: '5. Mitwirkungspflichten des Auftraggebers',
content: 'Der Auftraggeber stellt alle für die Leistungserbringung erforderlichen Informationen, Zugänge und Materialien rechtzeitig zur Verfügung. Verzögerungen, die auf fehlender Mitwirkung beruhen, gehen nicht zu Lasten des Auftragnehmers und können zu Terminverschiebungen führen.',
},
{
title: '6. Urheberrecht und Nutzungsrechte',
content: 'Mit vollständiger Bezahlung gehen die vereinbarten Nutzungsrechte an den erstellten Werken auf den Auftraggeber über. Der Auftragnehmer behält das Recht, die erstellten Arbeiten als Referenz im Portfolio zu verwenden, sofern nicht ausdrücklich anders vereinbart. Quellcode und Dokumentation werden nach Abschluss und Bezahlung vollständig übergeben.',
},
{
title: '7. Gewährleistung und Haftung',
content: 'Die Gewährleistung richtet sich nach den gesetzlichen Bestimmungen. Mängel sind unverzüglich nach Entdeckung schriftlich anzuzeigen. Die Haftung für leichte Fahrlässigkeit ist auf den vorhersehbaren, vertragstypischen Schaden begrenzt, soweit es sich nicht um die Verletzung wesentlicher Vertragspflichten, Schäden aus der Verletzung des Lebens, des Körpers oder der Gesundheit handelt.',
},
{
title: '8. Vertraulichkeit und Datenschutz',
content: 'Beide Parteien verpflichten sich, alle im Rahmen der Zusammenarbeit erhaltenen vertraulichen Informationen geheim zu halten. Die Verarbeitung personenbezogener Daten erfolgt im Einklang mit der DSGVO. Weitere Informationen finden Sie in der Datenschutzerklärung.',
},
{
title: '9. Kündigung',
content: 'Projektverträge können von beiden Seiten mit einer Frist von 14 Tagen zum Monatsende schriftlich gekündigt werden, sofern nicht anders vereinbart. Bereits erbrachte Leistungen sind in jedem Fall zu vergüten. Das Recht zur außerordentlichen Kündigung aus wichtigem Grund bleibt unberührt.',
},
{
title: '10. Schlussbestimmungen',
content: 'Es gilt das Recht der Bundesrepublik Deutschland. Erfüllungsort und Gerichtsstand ist Köln, soweit gesetzlich zulässig. Sollten einzelne Bestimmungen dieser AGB unwirksam sein oder werden, bleibt die Wirksamkeit der übrigen Bestimmungen unberührt.',
},
],
contact: {
title: 'Kontakt',
content: [
'Bei Fragen zu diesen AGB erreichen Sie mich unter:',
'Damjan Savić',
'E-Mail: info@damjan-savic.com',
],
},
privacyNote: {
text: 'Informationen zum Datenschutz finden Sie in unserer',
link: 'Datenschutzerklärung',
},
},
en: {
title: 'Terms and Conditions',
lastUpdated: 'Last updated: February 2026',
sections: [
{
title: '1. Scope of Application',
content: 'These Terms and Conditions apply to all services and offerings by Damjan Savić in the fields of fullstack development, AI development, process automation and IT consulting, as well as the use of the website damjan-savic.com.',
},
{
title: '2. Scope of Services',
content: 'The scope of services to be provided is determined by the respective project agreement or offer. Services include but are not limited to:',
items: [
'Development of web applications and SaaS solutions (Next.js, React, TypeScript)',
'AI development and integration (Claude AI, GPT, Voice AI)',
'Process automation (n8n, Make, custom solutions)',
'IT consulting and technical concept development',
'Maintenance and support of existing applications',
],
},
{
title: '3. Offers and Contract Formation',
content: 'Offers are non-binding and subject to change. A contract is only concluded upon written confirmation or commencement of service delivery. Oral side agreements require written confirmation.',
},
{
title: '4. Remuneration and Payment Terms',
content: 'Remuneration is based on the respective agreement. Invoices are payable within 14 days of invoicing without deductions. In case of late payment, default interest at the statutory rate will be charged. For larger projects, progress payments may be agreed upon.',
},
{
title: '5. Client Cooperation Obligations',
content: 'The client shall provide all information, access, and materials required for service delivery in a timely manner. Delays resulting from lack of cooperation are not attributable to the contractor and may lead to schedule adjustments.',
},
{
title: '6. Copyright and Usage Rights',
content: 'Upon full payment, the agreed usage rights to the created works are transferred to the client. The contractor retains the right to use the created works as portfolio references, unless expressly agreed otherwise. Source code and documentation are fully handed over upon completion and payment.',
},
{
title: '7. Warranty and Liability',
content: 'Warranty is governed by statutory provisions. Defects must be reported in writing immediately upon discovery. Liability for slight negligence is limited to foreseeable, contract-typical damage, unless it concerns the breach of essential contractual obligations or damages resulting from injury to life, body, or health.',
},
{
title: '8. Confidentiality and Data Protection',
content: 'Both parties commit to keeping all confidential information received in the course of cooperation secret. The processing of personal data is carried out in accordance with the GDPR. Further information can be found in the Privacy Policy.',
},
{
title: '9. Termination',
content: 'Project contracts may be terminated by either party with 14 days\' notice to the end of the month in writing, unless otherwise agreed. Services already rendered must be compensated in any case. The right to extraordinary termination for good cause remains unaffected.',
},
{
title: '10. Final Provisions',
content: 'The laws of the Federal Republic of Germany apply. The place of performance and jurisdiction is Cologne (Köln), to the extent permitted by law. Should individual provisions of these Terms and Conditions be or become invalid, the validity of the remaining provisions shall remain unaffected.',
},
],
contact: {
title: 'Contact',
content: [
'For questions about these Terms and Conditions, please contact:',
'Damjan Savić',
'Email: info@damjan-savic.com',
],
},
privacyNote: {
text: 'For information on data protection, please see our',
link: 'Privacy Policy',
},
},
sr: {
title: 'Uslovi korišćenja',
lastUpdated: 'Poslednje ažuriranje: Februar 2026',
sections: [
{
title: '1. Oblast primene',
content: 'Ovi Uslovi korišćenja se primenjuju na sve usluge i ponude Damjana Savića u oblastima fullstack razvoja, AI razvoja, automatizacije procesa i IT konsaltinga, kao i na korišćenje web stranice damjan-savic.com.',
},
{
title: '2. Obim usluga',
content: 'Obim usluga koje treba pružiti određuje se odgovarajućim projektnim ugovorom ili ponudom. Usluge uključuju, ali nisu ograničene na:',
items: [
'Razvoj veb aplikacija i SaaS rešenja (Next.js, React, TypeScript)',
'AI razvoj i integracija (Claude AI, GPT, Voice AI)',
'Automatizacija procesa (n8n, Make, prilagođena rešenja)',
'IT konsalting i razvoj tehničkih koncepata',
'Održavanje i podrška postojećih aplikacija',
],
},
{
title: '3. Ponude i zaključenje ugovora',
content: 'Ponude su neobavezujuće i podložne promenama. Ugovor se zaključuje tek pisanom potvrdom ili početkom pružanja usluga. Usmeni sporedni dogovori zahtevaju pisanu potvrdu.',
},
{
title: '4. Naknada i uslovi plaćanja',
content: 'Naknada se zasniva na odgovarajućem dogovoru. Fakture se plaćaju u roku od 14 dana od izdavanja bez odbitaka. U slučaju kašnjenja s plaćanjem, zaračunavaju se zakonske zatezne kamate. Za veće projekte mogu se dogovoriti avansna plaćanja.',
},
{
title: '5. Obaveze saradnje klijenta',
content: 'Klijent je dužan da blagovremeno obezbedi sve informacije, pristupe i materijale potrebne za pružanje usluga. Kašnjenja nastala usled nedostatka saradnje ne pripisuju se izvršiocu i mogu dovesti do prilagođavanja rokova.',
},
{
title: '6. Autorska prava i prava korišćenja',
content: 'Po izvršenoj uplati, dogovorena prava korišćenja kreiranih dela prenose se na klijenta. Izvršilac zadržava pravo da koristi kreirana dela kao referentne projekte u portfoliju, osim ako je izričito drugačije dogovoreno. Izvorni kod i dokumentacija se u potpunosti predaju po završetku i plaćanju.',
},
{
title: '7. Garancija i odgovornost',
content: 'Garancija se reguliše zakonskim odredbama. Nedostaci se moraju prijaviti u pisanoj formi odmah po otkrivanju. Odgovornost za blagi nemar ograničena je na predvidivu, ugovorom tipičnu štetu, osim ako se radi o kršenju suštinskih ugovornih obaveza ili štetama nastalim povredom života, tela ili zdravlja.',
},
{
title: '8. Poverljivost i zaštita podataka',
content: 'Obe strane se obavezuju da će čuvati sve poverljive informacije primljene tokom saradnje. Obrada ličnih podataka vrši se u skladu sa GDPR-om. Više informacija možete pronaći u Politici privatnosti.',
},
{
title: '9. Raskid ugovora',
content: 'Projektni ugovori mogu se raskinuti od strane bilo koje strane sa rokom od 14 dana do kraja meseca u pisanoj formi, osim ako nije drugačije dogovoreno. Već pružene usluge moraju se u svakom slučaju nadoknaditi. Pravo na vanredan raskid iz opravdanog razloga ostaje nepromenjeno.',
},
{
title: '10. Završne odredbe',
content: 'Primenjuju se zakoni Savezne Republike Nemačke. Mesto ispunjenja i nadležnost je Keln (Köln), u meri dozvoljenoj zakonom. Ukoliko pojedine odredbe ovih Uslova korišćenja budu ili postanu nevažeće, važnost ostalih odredbi ostaje nepromenjena.',
},
],
contact: {
title: 'Kontakt',
content: [
'Za pitanja o ovim Uslovima korišćenja, kontaktirajte:',
'Damjan Savić',
'Email: info@damjan-savic.com',
],
},
privacyNote: {
text: 'Za informacije o zaštiti podataka, pogledajte našu',
link: 'Politiku privatnosti',
},
},
};
const c = content[locale] || content.de;
return (
<main className="min-h-screen py-20 px-4">
<div className="max-w-4xl mx-auto">
<main className="min-h-screen py-16 sm:py-20 lg:py-24">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
{/* Breadcrumb */}
<nav className="mb-8">
<Link href={`/${locale}`} className="text-zinc-400 hover:text-white transition-colors">
<Link
href={`/${locale}`}
className="text-zinc-400 hover:text-white transition-colors"
>
Home
</Link>
<span className="text-zinc-600 mx-2">/</span>
<span className="text-white">{t('content.title')}</span>
<span className="mx-2 text-zinc-600">/</span>
<span className="text-white">{c.title}</span>
</nav>
<article className="prose prose-invert prose-zinc max-w-none">
<h1>{t('content.title')}</h1>
<p className="text-zinc-400">{t('content.lastUpdated')}</p>
<h1 className="text-4xl font-bold text-white mb-2">{c.title}</h1>
<p className="text-zinc-400 mb-8">{c.lastUpdated}</p>
<h2>1. Agreement to Terms</h2>
<p>
By accessing our website, you agree to be bound by these Terms and Conditions
and to comply with all applicable laws and regulations.
</p>
<div className="prose prose-invert max-w-none space-y-8">
{c.sections.map((section, index) => (
<section key={index}>
<h2 className="text-2xl font-semibold text-white mb-4">{section.title}</h2>
<p className="text-zinc-300">{section.content}</p>
{section.items && (
<ul className="list-disc list-inside text-zinc-300 space-y-1 ml-4 mt-3">
{section.items.map((item, itemIndex) => (
<li key={itemIndex}>{item}</li>
))}
</ul>
)}
</section>
))}
<h2>2. Intellectual Property</h2>
<p>
All content on this website, including text, graphics, logos, images, and software,
is the property of Damjan Savić and is protected by intellectual property laws.
</p>
{/* Contact */}
<section className="bg-zinc-900/50 p-6 rounded-xl ring-1 ring-zinc-800">
<h2 className="text-2xl font-semibold text-white mb-4">{c.contact.title}</h2>
<div className="text-zinc-300 space-y-1">
{c.contact.content.map((line, index) => (
<p key={index}>{line}</p>
))}
</div>
</section>
<h2>3. User Responsibilities</h2>
<p>
When using our website, you agree to provide accurate information,
use the website legally and ethically, and not attempt unauthorized access.
</p>
{/* Privacy link */}
<section>
<p className="text-zinc-400">
{c.privacyNote.text}{' '}
<Link
href={`/${locale}/privacy`}
className="text-orange-500 hover:text-orange-400 underline"
>
{c.privacyNote.link}
</Link>
.
</p>
</section>
<h2>4. Limitation of Liability</h2>
<p>
We are not liable for any indirect, incidental, special, or consequential damages
arising from your use of or inability to use the website.
</p>
<h2>5. Governing Law</h2>
<p>
These terms are governed by and construed in accordance with the laws of Germany.
</p>
<h2>6. Contact</h2>
<p>
For questions about these terms, please contact us at:
<br />
Email: legal@damjan-savic.com
</p>
</article>
{/* Copyright */}
<section>
<p className="text-zinc-400">
&copy; {new Date().getFullYear()} Damjan Savić.{' '}
{locale === 'de' ? 'Alle Rechte vorbehalten.' : locale === 'sr' ? 'Sva prava zadržana.' : 'All rights reserved.'}
</p>
</section>
</div>
</div>
</main>
);
+182 -120
View File
@@ -1,132 +1,194 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--font-inter: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
/* Animation Design Tokens */
--transition-fast: 150ms;
--transition-base: 200ms;
--transition-slow: 300ms;
--transition-slower: 500ms;
/* Colors */
--background: 24 24 27;
--foreground: 250 250 250;
--card: 39 39 42;
--card-foreground: 250 250 250;
--primary: 59 130 246;
--primary-foreground: 255 255 255;
--secondary: 63 63 70;
--secondary-foreground: 250 250 250;
--muted: 63 63 70;
--muted-foreground: 161 161 170;
--accent: 63 63 70;
--accent-foreground: 250 250 250;
--destructive: 239 68 68;
--destructive-foreground: 250 250 250;
--border: 63 63 70;
--input: 63 63 70;
--ring: 59 130 246;
@import "tailwindcss";
@source "../../src";
@plugin "tailwindcss-animate";
@plugin "@tailwindcss/forms" {
strategy: class;
}
/* Prefers-Reduced-Motion Support */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
scroll-behavior: auto !important;
@theme {
--font-sans: 'Inter', system-ui, sans-serif;
/* Primary Colors */
--color-primary: #697565;
--color-primary-dark: #181C14;
--color-primary-foreground: #ECDFCC;
/* Secondary Colors */
--color-secondary: #3C3D37;
--color-secondary-gray: #3C3D37;
--color-secondary-fuzzy: #465B50;
--color-secondary-foreground: #ECDFCC;
/* Text Colors */
--color-text: #ECDFCC;
--color-text-light: #697565;
/* System Colors */
--color-background: #181C14;
--color-foreground: #ECDFCC;
--color-card: #1E221A;
--color-card-foreground: #ECDFCC;
--color-popover: #1E221A;
--color-popover-foreground: #ECDFCC;
--color-muted: #3C3D37;
--color-muted-foreground: #697565;
--color-accent: #465B50;
--color-accent-foreground: #ECDFCC;
--color-destructive: #B43C3C;
--color-destructive-foreground: #ECDFCC;
--color-border: #3C3D37;
--color-input: #3C3D37;
--color-ring: #465B50;
/* Border Radius */
--radius-lg: 0.5rem;
--radius-md: calc(0.5rem - 2px);
--radius-sm: calc(0.5rem - 4px);
/* Animations */
--animate-slide-up: slideUp 0.5s ease-out forwards;
--animate-fade-in: fadeIn 0.3s ease-out forwards;
}
@keyframes slideUp {
0% { transform: translateY(10px); }
100% { transform: translateY(0); }
}
@keyframes fadeIn {
0% { opacity: 0; }
100% { opacity: 1; }
}
/*
All custom styles MUST be inside @layer blocks.
In Tailwind v4, unlayered styles override ALL layered utilities.
*/
@layer base {
:root {
--font-inter: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
/* Animation Design Tokens */
--transition-fast: 150ms;
--transition-base: 200ms;
--transition-slow: 300ms;
--transition-slower: 500ms;
/* Navigation Colors */
--nav-bg: #181C14;
--nav-bg-transparent: rgb(24 28 20 / 0.95);
--nav-text: #ECDFCC;
--nav-text-hover: #465B50;
--nav-text-active: #465B50;
--nav-border: #3C3D37;
--nav-shadow: rgb(0 0 0 / 0.1);
--radius: 0.5rem;
}
html {
scroll-behavior: smooth;
}
body {
font-family: var(--font-inter);
background-color: var(--color-background);
color: var(--color-foreground);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* Custom scrollbar */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: var(--color-background);
}
::-webkit-scrollbar-thumb {
background: var(--color-secondary);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--color-muted-foreground);
}
/* Selection */
::selection {
background-color: rgb(105 117 101 / 0.3);
}
/* Focus styles */
:focus-visible {
outline: 2px solid var(--color-ring);
outline-offset: 2px;
}
/* Smooth transitions */
a,
button {
transition: color 0.2s ease, background-color 0.2s ease, border-color 0.2s ease;
}
}
* {
box-sizing: border-box;
padding: 0;
margin: 0;
/* Prefers-Reduced-Motion Support - must be outside layers for !important to work correctly */
@media (prefers-reduced-motion: reduce) {
@layer base {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
scroll-behavior: auto !important;
}
}
}
html {
scroll-behavior: smooth;
@layer components {
/* Utility classes */
.text-gradient {
background: linear-gradient(135deg, var(--color-primary) 0%, #8b5cf6 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.glass {
background: rgb(30 34 26 / 0.8);
backdrop-filter: blur(12px);
border: 1px solid rgb(60 61 55 / 0.5);
}
/* Safe Area Insets for notches */
.mobile-safe {
padding-bottom: max(1rem, env(safe-area-inset-bottom));
}
.mobile-safe-top {
padding-top: max(1rem, env(safe-area-inset-top));
}
}
body {
font-family: var(--font-inter);
background-color: rgb(var(--background));
color: rgb(var(--foreground));
min-height: 100vh;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
@layer utilities {
/* Will-Change for Performance */
.animate-spin {
will-change: transform;
}
/* Custom scrollbar */
::-webkit-scrollbar {
width: 8px;
}
.animate-pulse {
will-change: opacity;
}
::-webkit-scrollbar-track {
background: rgb(var(--background));
}
::-webkit-scrollbar-thumb {
background: rgb(var(--secondary));
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: rgb(var(--muted-foreground));
}
/* Selection */
::selection {
background-color: rgb(var(--primary) / 0.3);
}
/* Focus styles */
:focus-visible {
outline: 2px solid rgb(var(--ring));
outline-offset: 2px;
}
/* Smooth transitions */
a,
button {
transition: color 0.2s ease, background-color 0.2s ease, border-color 0.2s ease;
}
/* Utility classes */
.text-gradient {
background: linear-gradient(135deg, rgb(var(--primary)) 0%, #8b5cf6 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.glass {
background: rgb(var(--card) / 0.8);
backdrop-filter: blur(12px);
border: 1px solid rgb(var(--border) / 0.5);
}
/* Safe Area Insets for notches */
.mobile-safe {
padding-bottom: max(1rem, env(safe-area-inset-bottom));
}
.mobile-safe-top {
padding-top: max(1rem, env(safe-area-inset-top));
}
/* Will-Change for Performance */
.animate-spin {
will-change: transform;
}
.animate-pulse {
will-change: opacity;
}
.animate-bounce {
will-change: transform;
.animate-bounce {
will-change: transform;
}
}
-1
View File
@@ -9,7 +9,6 @@ export default function robots(): MetadataRoute.Robots {
userAgent: '*',
allow: '/',
disallow: ['/login', '/dashboard', '/api/', '/admin', '/.env'],
crawlDelay: 1,
},
],
sitemap: `${baseUrl}/sitemap.xml`,
+18
View File
@@ -2,6 +2,7 @@ import { MetadataRoute } from 'next';
import { locales } from '@/i18n/config';
import { getAllCitySlugs } from '@/data/cities';
import { getAllServiceSlugs } from '@/data/services';
import { getAllTechnologySlugs } from '@/data/technologies';
const BASE_URL = process.env.NEXT_PUBLIC_SITE_URL || 'https://damjan-savic.com';
@@ -109,6 +110,23 @@ export default function sitemap(): MetadataRoute.Sitemap {
});
});
// Add technology pages
const techSlugs = getAllTechnologySlugs();
techSlugs.forEach((slug) => {
locales.forEach((locale) => {
const localePath = locale === 'de' ? `/leistungen/technologie/${slug}` : `/${locale}/leistungen/technologie/${slug}`;
sitemap.push({
url: `${BASE_URL}${localePath}`,
lastModified: today,
changeFrequency: 'monthly',
priority: 0.8,
alternates: {
languages: generateAlternates(`/leistungen/technologie/${slug}`),
},
});
});
});
// Add blog posts
blogSlugs.forEach((slug) => {
locales.forEach((locale) => {
+34 -34
View File
@@ -1,56 +1,56 @@
@layer base {
:root {
/* Primary Colors */
--color-primary: 105 117 101; /* #697565 - Sage green */
--color-primary-dark: 24 28 20; /* #181C14 - Dark moss */
--color-primary: #697565; /* Sage green */
--color-primary-dark: #181C14; /* Dark moss */
/* Secondary Colors */
--color-secondary: 24 28 20; /* #181C14 - Dark moss */
--color-secondary-gray: 60 61 55; /* #3C3D37 - Charcoal */
--color-secondary-fuzzy: 70 91 80; /* #465B50 - Dark sage */
--color-secondary: #181C14; /* Dark moss */
--color-secondary-gray: #3C3D37; /* Charcoal */
--color-secondary-fuzzy: #465B50; /* Dark sage */
/* Text Colors */
--color-text: 236 223 204; /* #ECDFCC - Cream */
--color-text-light: 105 117 101; /* #697565 - Sage */
--color-text: #ECDFCC; /* Cream */
--color-text-light: #697565; /* Sage */
/* Navigation Colors */
--nav-bg: 24 28 20; /* Dark moss */
--nav-bg-transparent: 24 28 20 / 0.95; /* Dark moss with transparency */
--nav-text: 236 223 204; /* Cream */
--nav-text-hover: 70 91 80; /* Dark sage */
--nav-text-active: 70 91 80; /* Dark sage */
--nav-border: 60 61 55; /* Charcoal */
--nav-shadow: 0 0 0 / 0.1;
--nav-bg: #181C14; /* Dark moss */
--nav-bg-transparent: rgb(24 28 20 / 0.95); /* Dark moss with transparency */
--nav-text: #ECDFCC; /* Cream */
--nav-text-hover: #465B50; /* Dark sage */
--nav-text-active: #465B50; /* Dark sage */
--nav-border: #3C3D37; /* Charcoal */
--nav-shadow: rgb(0 0 0 / 0.1);
/* System Colors */
--background: 24 28 20; /* Dark moss */
--foreground: 236 223 204; /* Cream */
--background: #181C14; /* Dark moss */
--foreground: #ECDFCC; /* Cream */
--card: 30 34 26; /* Slightly lighter dark moss */
--card-foreground: 236 223 204; /* Cream */
--card: #1E221A; /* Slightly lighter dark moss */
--card-foreground: #ECDFCC; /* Cream */
--popover: 30 34 26; /* Slightly lighter dark moss */
--popover-foreground: 236 223 204; /* Cream */
--popover: #1E221A; /* Slightly lighter dark moss */
--popover-foreground: #ECDFCC; /* Cream */
--primary: 105 117 101; /* Sage */
--primary-foreground: 236 223 204; /* Cream */
--primary: #697565; /* Sage */
--primary-foreground: #ECDFCC; /* Cream */
--secondary: 60 61 55; /* Charcoal */
--secondary-foreground: 236 223 204; /* Cream */
--secondary: #3C3D37; /* Charcoal */
--secondary-foreground: #ECDFCC; /* Cream */
--muted: 60 61 55; /* Charcoal */
--muted-foreground: 105 117 101; /* Sage */
--muted: #3C3D37; /* Charcoal */
--muted-foreground: #697565; /* Sage */
--accent: 70 91 80; /* Dark sage - neuer dunkelgrüner Akzent */
--accent-foreground: 236 223 204; /* Cream */
--accent: #465B50; /* Dark sage */
--accent-foreground: #ECDFCC; /* Cream */
--destructive: 180 60 60; /* Muted red for destructive actions */
--destructive-foreground: 236 223 204; /* Cream */
--destructive: #B43C3C; /* Muted red for destructive actions */
--destructive-foreground: #ECDFCC; /* Cream */
--border: 60 61 55; /* Charcoal */
--input: 60 61 55; /* Charcoal */
--ring: 70 91 80; /* Dark sage */
--border: #3C3D37; /* Charcoal */
--input: #3C3D37; /* Charcoal */
--ring: #465B50; /* Dark sage */
--radius: 0.5rem;
}
}
}
+6 -6
View File
@@ -125,7 +125,7 @@ export function ContactForm() {
return (
<div className="min-h-screen">
{/* Hero Section */}
<div className="py-24 text-center">
<div className="py-12 sm:py-16 md:py-24 text-center">
<motion.h1
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
@@ -145,7 +145,7 @@ export function ContactForm() {
{/* Main Content */}
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pb-24">
<div className="grid grid-cols-1 md:grid-cols-2 gap-12">
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-12">
{/* Contact Info */}
<motion.div
initial={{ opacity: 0, x: -20 }}
@@ -155,17 +155,17 @@ export function ContactForm() {
>
{/* Headshot */}
<div className="flex items-center gap-6">
<div className="relative w-24 h-24 rounded-full overflow-hidden border-2 border-zinc-700 flex-shrink-0">
<div className="relative w-16 h-16 sm:w-20 sm:h-20 md:w-24 md:h-24 rounded-full overflow-hidden border-2 border-zinc-700 flex-shrink-0">
<Image
src="/images/headshot.avif"
alt="Damjan Savić - Full-Stack Web Developer"
fill
sizes="96px"
sizes="(max-width: 640px) 64px, (max-width: 768px) 80px, 96px"
className="object-cover"
/>
</div>
<div>
<h2 className="text-2xl font-bold text-white mb-1">
<div className="min-w-0">
<h2 className="text-2xl font-bold text-white mb-1 break-words">
{t('contactInfo.title')}
</h2>
<p className="text-zinc-400">
+3 -3
View File
@@ -8,9 +8,9 @@ const Footer = async () => {
const currentYear = new Date().getFullYear();
return (
<footer className="relative bg-zinc-800/50 backdrop-blur-sm border-t border-zinc-700/30 pt-8 pb-4 px-4">
<footer className="relative bg-zinc-800/50 backdrop-blur-sm border-t border-zinc-700/30 pt-6 sm:pt-8 pb-4 px-4 sm:px-6 lg:px-8">
<div className="max-w-7xl mx-auto">
<div className="flex flex-col md:grid md:grid-cols-2 lg:grid-cols-4 gap-8">
<div className="flex flex-col sm:grid sm:grid-cols-2 lg:grid-cols-4 gap-6 sm:gap-8">
{/* Contact Section */}
<div>
<h3 className="text-white text-base font-semibold mb-3">
@@ -92,7 +92,7 @@ const Footer = async () => {
</div>
{/* Legal Section */}
<div className="border-t border-zinc-700/30 pt-4">
<div className="border-t sm:border-t-0 lg:border-t border-zinc-700/30 pt-4 sm:pt-0 lg:pt-4">
<div className="flex flex-col space-y-4">
<p className="text-zinc-400 text-xs text-left">
&copy; {currentYear} Damjan Savić. {t('legal.copyright')}
+5 -5
View File
@@ -102,7 +102,7 @@ const Header = () => {
aria-label="Main navigation"
>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center h-16">
<div className="flex justify-between items-center h-14 sm:h-16">
{/* Logo */}
<Link href={`/${locale}`} className="flex items-center space-x-2 group shrink-0">
<Image
@@ -156,12 +156,12 @@ const Header = () => {
{/* Mobile Sidebar */}
<div
id="mobile-menu"
className={`fixed right-0 top-0 h-full w-80 bg-zinc-900/95 backdrop-blur-md shadow-xl md:hidden z-50 border-l border-zinc-800 transition-transform duration-300 ease-out ${
className={`fixed right-0 top-0 h-full w-[min(20rem,90vw)] bg-zinc-900/95 backdrop-blur-md shadow-xl md:hidden z-50 border-l border-zinc-800 transition-transform duration-300 ease-out ${
isMenuOpen ? 'translate-x-0' : 'translate-x-full'
}`}
>
{/* Sidebar Header */}
<div className="flex items-center justify-between px-4 h-16 border-b border-zinc-800">
<div className="flex items-center justify-between px-4 h-14 sm:h-16 border-b border-zinc-800">
<span className="text-white font-semibold">Menu</span>
<button
onClick={() => setIsMenuOpen(false)}
@@ -173,13 +173,13 @@ const Header = () => {
</div>
{/* Scrollable Sidebar Content */}
<div className="h-[calc(100vh-4rem)] overflow-y-auto mobile-safe">
<div className="h-[calc(100vh-3.5rem)] sm:h-[calc(100vh-4rem)] overflow-y-auto mobile-safe">
<div className="flex flex-col h-full">
{/* Contact Info */}
<ContactInfo />
{/* Navigation Links */}
<div className="py-4 px-4">
<div className="py-3 px-3 sm:py-4 sm:px-4">
{navigationLinks.map(({ path, label, icon }) => (
<NavLink
key={path}
+1 -1
View File
@@ -97,7 +97,7 @@ const LanguageSwitcher = () => {
</button>
<div
className={`absolute ${isMobile ? 'bottom-full mb-2' : 'top-full mt-2'} right-0 w-48 rounded-lg overflow-hidden
className={`absolute ${isMobile ? 'bottom-full mb-2' : 'top-full mt-2'} right-0 w-44 sm:w-48 max-w-[calc(100vw-2rem)] rounded-lg overflow-hidden
border border-zinc-800 bg-zinc-900/95 backdrop-blur-sm shadow-lg
transition-all duration-200 origin-top
${isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-95 pointer-events-none'}`}
+4 -4
View File
@@ -20,14 +20,14 @@ const About = async () => {
];
return (
<section id="about" className="py-24 relative">
<section id="about" className="py-12 sm:py-16 md:py-24 relative">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 md:gap-12 items-center">
{/* Content Section */}
<div className="space-y-6 order-2 lg:order-1">
{/* Title */}
<div>
<h2 className="text-4xl lg:text-5xl font-bold text-white mb-2">
<h2 className="text-3xl sm:text-4xl lg:text-5xl font-bold text-white mb-2">
{t('title')}
</h2>
<div className="h-1 w-20 bg-gradient-to-r from-zinc-600 to-zinc-800 rounded-full" />
@@ -80,7 +80,7 @@ const About = async () => {
src="/images/gallery/frontal-smile.avif"
alt="Damjan Savić"
fill
sizes="(max-width: 1024px) 100vw, 50vw"
sizes="(max-width: 1023px) 90vw, 50vw"
className="object-cover object-bottom"
/>
</div>
+5 -6
View File
@@ -73,7 +73,7 @@ const Experience = () => {
viewport={{ once: true }}
transition={{ duration: 0.8, ease: [0.25, 0.46, 0.45, 0.94] }}
>
<h2 className="text-3xl font-bold text-white text-center mb-12">
<h2 className="text-2xl sm:text-3xl md:text-4xl font-bold text-white text-center mb-8 sm:mb-12">
{t('title')}
</h2>
<div
@@ -135,7 +135,7 @@ const Experience = () => {
<div className="flex justify-center items-center gap-4 mt-8">
<button
onClick={() => currentPage > 0 && setCurrentPage(prev => prev - 1)}
className="text-zinc-400 hover:text-white transition-all duration-300 p-2 text-2xl font-light disabled:opacity-30"
className="text-zinc-400 hover:text-white transition-all duration-300 p-3 sm:p-2 text-2xl font-light disabled:opacity-30"
disabled={currentPage === 0}
aria-label="Previous page"
>
@@ -147,21 +147,20 @@ const Experience = () => {
key={index}
onClick={() => setCurrentPage(index)}
className="relative flex items-center justify-center transition-all duration-300"
style={{ width: '24px', height: '24px' }}
className="relative flex items-center justify-center w-10 h-10 sm:w-6 sm:h-6 transition-all duration-300"
aria-label={`Page ${index + 1}`}
>
<span
className={`rounded-full transition-all duration-300 ${
index === currentPage ? 'bg-white' : 'bg-zinc-600 hover:bg-zinc-400'
index === currentPage ? 'bg-white w-2.5 h-2.5' : 'bg-zinc-600 hover:bg-zinc-400 w-2 h-2'
}`}
style={{ width: '8px', height: '8px' }}
/>
</button>
))}
</div>
<button
onClick={() => currentPage < totalPages - 1 && setCurrentPage(prev => prev + 1)}
className="text-zinc-400 hover:text-white transition-all duration-300 p-2 text-2xl font-light disabled:opacity-30"
className="text-zinc-400 hover:text-white transition-all duration-300 p-3 sm:p-2 text-2xl font-light disabled:opacity-30"
disabled={currentPage === totalPages - 1}
aria-label="Next page"
>
+14 -13
View File
@@ -24,7 +24,7 @@ const Hero = async () => {
</div>
{/* Social Links Bar */}
<div className="absolute top-20 left-0 right-0 px-4 sm:px-8" style={{ zIndex: 40 }}>
<div className="absolute top-20 left-0 right-0 px-4 sm:px-6 lg:px-8" style={{ zIndex: 40 }}>
<div className="max-w-7xl mx-auto flex justify-between items-center">
<div className="flex gap-4">
<a
@@ -56,10 +56,10 @@ const Hero = async () => {
</div>
{/* Main Content */}
<div className="flex flex-col items-center justify-start min-h-screen px-4 pt-32 relative" style={{ zIndex: 30 }}>
<div className="flex flex-col items-center justify-start min-h-screen px-4 sm:px-6 pt-24 sm:pt-32 relative" style={{ zIndex: 30 }}>
{/* Profile Image */}
<div
className="w-56 h-56 sm:w-72 sm:h-72 mb-8 rounded-full overflow-hidden border-2 border-zinc-800"
className="w-44 h-44 sm:w-56 sm:h-56 md:w-64 md:h-64 lg:w-72 lg:h-72 mb-6 sm:mb-8 rounded-full overflow-hidden border-2 border-zinc-800"
style={{ transform: 'translateZ(0)' }}
>
<Image
@@ -67,51 +67,52 @@ const Hero = async () => {
alt={t('image.alt')}
width={288}
height={288}
sizes="(max-width: 640px) 176px, (max-width: 768px) 224px, (max-width: 1024px) 256px, 288px"
className="w-full h-full object-cover"
priority
/>
</div>
{/* Title and Name */}
<p className="text-zinc-400 text-sm sm:text-base tracking-wider mb-2">
<p className="text-zinc-400 text-xs sm:text-sm md:text-base tracking-wider mb-2">
{t('title')}
</p>
<h1 className="text-4xl sm:text-5xl font-bold mb-3 flex items-center">
<h1 className="text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-bold mb-3 flex items-center">
{t('name')}<span className="animate-pulse ml-1">|</span>
</h1>
<p className="text-zinc-300 text-sm sm:text-base max-w-xl text-center mb-2 px-4">
<p className="text-zinc-300 text-sm sm:text-base max-w-xl text-center mb-2 px-2">
{t('description')}
</p>
<p className="text-zinc-400 text-xs sm:text-sm mb-8">
<p className="text-zinc-400 text-xs sm:text-sm mb-6 sm:mb-8">
{t('currentRole')}
</p>
{/* Navigation */}
<div className="flex flex-col sm:flex-row gap-4 sm:gap-8 text-white text-base sm:text-lg z-20">
<div className="flex flex-wrap justify-center gap-3 sm:gap-4 md:gap-6 text-white text-sm sm:text-base md:text-lg z-20 w-full max-w-2xl">
<a
href="#experience"
className="relative w-full sm:w-auto px-8 py-3 rounded-full uppercase cursor-pointer overflow-hidden group text-center"
className="relative px-5 sm:px-8 py-2.5 sm:py-3 rounded-full uppercase cursor-pointer overflow-hidden group text-center"
>
<span className="relative z-10">{t('navigation.experience')}</span>
<div className="absolute inset-0 bg-zinc-700/50 opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-full" />
</a>
<a
href="#skills"
className="relative w-full sm:w-auto px-8 py-3 rounded-full uppercase cursor-pointer overflow-hidden group text-center"
className="relative px-5 sm:px-8 py-2.5 sm:py-3 rounded-full uppercase cursor-pointer overflow-hidden group text-center"
>
<span className="relative z-10">{t('navigation.skills')}</span>
<div className="absolute inset-0 bg-zinc-700/50 opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-full" />
</a>
<a
href="#projects"
className="relative w-full sm:w-auto px-8 py-3 rounded-full uppercase cursor-pointer overflow-hidden group text-center"
className="relative px-5 sm:px-8 py-2.5 sm:py-3 rounded-full uppercase cursor-pointer overflow-hidden group text-center"
>
<span className="relative z-10">{t('navigation.projects')}</span>
<div className="absolute inset-0 bg-zinc-700/50 opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-full" />
</a>
<a
href="#about"
className="relative w-full sm:w-auto px-8 py-3 rounded-full uppercase cursor-pointer overflow-hidden group text-center"
className="relative px-5 sm:px-8 py-2.5 sm:py-3 rounded-full uppercase cursor-pointer overflow-hidden group text-center"
>
<span className="relative z-10">{t('navigation.about')}</span>
<div className="absolute inset-0 bg-zinc-700/50 opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-full" />
@@ -120,7 +121,7 @@ const Hero = async () => {
</div>
{/* Contact Button Bottom */}
<div className="absolute bottom-8 left-1/2 transform -translate-x-1/2" style={{ zIndex: 40 }}>
<div className="absolute bottom-6 sm:bottom-8 left-1/2 transform -translate-x-1/2" style={{ zIndex: 40 }}>
<a
href="mailto:info@damjan-savic.com"
className="w-12 h-12 flex items-center justify-center rounded-full bg-white/10 hover:bg-white/20 transition-colors"
+5 -5
View File
@@ -36,10 +36,10 @@ const Projects = async () => {
const locale = await getLocale();
return (
<section id="projects" className="py-24">
<section id="projects" className="py-12 sm:py-16 md:py-24">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center mb-12">
<h2 className="text-3xl font-bold text-zinc-100">
<div className="flex justify-between items-center mb-8 sm:mb-12">
<h2 className="text-2xl sm:text-3xl md:text-4xl font-bold text-zinc-100">
{t('title')}
</h2>
<Link
@@ -50,7 +50,7 @@ const Projects = async () => {
</Link>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6">
{projectsData.map((project) => (
<Link
key={project.slug}
@@ -69,7 +69,7 @@ const Projects = async () => {
src={`/images/projects/${project.slug}/cover.avif`}
alt={project.title}
fill
sizes="(max-width: 640px) 350px, (max-width: 768px) 400px, (max-width: 1024px) 550px, 400px"
sizes="(max-width: 640px) 100vw, (max-width: 768px) 100vw, (max-width: 1024px) 50vw, 33vw"
className="object-cover transition-transform duration-700 group-hover:scale-110"
/>
</div>
+4 -4
View File
@@ -98,7 +98,7 @@ const Skills = () => {
}
return (
<section id="skills" className="py-24 relative">
<section id="skills" className="py-12 sm:py-16 md:py-24 relative">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<motion.div
initial={{ opacity: 0, y: 20 }}
@@ -106,10 +106,10 @@ const Skills = () => {
viewport={{ once: true }}
transition={{ duration: 0.6, ease: [0.25, 0.1, 0.25, 1] }}
>
<h2 className="text-3xl font-bold text-white mb-12">
<h2 className="text-2xl sm:text-3xl md:text-4xl font-bold text-white mb-8 sm:mb-12">
{t('title')}
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 sm:gap-6 md:gap-8">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 md:gap-8">
{/* Skills Progress Bars */}
<div className="space-y-6">
{skills.map((skill, idx) => (
@@ -145,7 +145,7 @@ const Skills = () => {
</div>
{/* Skills Icons Grid */}
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-3 gap-4 sm:gap-6">
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3 sm:gap-4 md:gap-6">
{skills.map((skill, idx) => (
<motion.div
key={skill.name}
+78 -78
View File
@@ -34,9 +34,9 @@ export const cities: CityData[] = [
sr: 'Kao AI developer i ekspert za automatizaciju iz Kelna, razvijam prilagođena rešenja za kompanije u rajnskoj metropoli. Od Voice AI do automatizacije procesa - vodim vaš posao na sledeći nivo.',
},
keywords: {
de: ['KI Entwickler Köln', 'AI Agentur Köln', 'Fullstack Entwickler Köln', 'Webentwickler Köln', 'Python Entwickler Köln'],
en: ['AI Developer Cologne', 'AI Agency Cologne', 'Fullstack Developer Cologne', 'Web Developer Cologne', 'Python Developer Cologne'],
sr: ['AI Developer Keln', 'Fullstack Developer Keln', 'Web Developer Keln'],
de: ['KI Entwickler Köln', 'AI Agentur Köln', 'Fullstack Entwickler Köln', 'Next.js Entwickler Köln', 'React Entwickler Köln', 'Claude AI Köln', 'TypeScript Entwickler Köln', 'Künstliche Intelligenz Köln', 'Webentwickler Köln', 'Python Entwickler Köln'],
en: ['AI Developer Cologne', 'AI Agency Cologne', 'Fullstack Developer Cologne', 'Next.js Developer Cologne', 'React Developer Cologne', 'Claude AI Developer Cologne', 'TypeScript Developer Cologne', 'Web Developer Cologne', 'Python Developer Cologne'],
sr: ['AI Developer Keln', 'Fullstack Developer Keln', 'Next.js Programer Keln', 'React Programer Keln', 'Claude AI Keln', 'Web Developer Keln'],
},
nearbyAreas: ['Bonn', 'Düsseldorf', 'Leverkusen', 'Bergisch Gladbach'],
},
@@ -54,9 +54,9 @@ export const cities: CityData[] = [
sr: 'Za kompanije u Diseldorfu nudim profesionalni AI razvoj i rešenja za automatizaciju. Glavni grad NRW-a poznat je po inovativnosti i ekonomskoj snazi - a ja vam pomažem da to iskoristite kroz pametnu tehnologiju.',
},
keywords: {
de: ['KI Entwickler Düsseldorf', 'AI Agentur Düsseldorf', 'Fullstack Entwickler Düsseldorf', 'Webentwickler Düsseldorf', 'Automatisierung Düsseldorf'],
en: ['AI Developer Düsseldorf', 'AI Agency Düsseldorf', 'Fullstack Developer Düsseldorf', 'Web Developer Düsseldorf', 'Automation Düsseldorf'],
sr: ['AI Developer Diseldorf', 'Fullstack Developer Diseldorf'],
de: ['KI Entwickler Düsseldorf', 'AI Agentur Düsseldorf', 'Fullstack Entwickler Düsseldorf', 'Next.js Entwickler Düsseldorf', 'React Entwickler Düsseldorf', 'Claude AI Düsseldorf', 'TypeScript Entwickler Düsseldorf', 'Künstliche Intelligenz Düsseldorf', 'Webentwickler Düsseldorf', 'Automatisierung Düsseldorf'],
en: ['AI Developer Düsseldorf', 'AI Agency Düsseldorf', 'Fullstack Developer Düsseldorf', 'Next.js Developer Düsseldorf', 'React Developer Düsseldorf', 'Claude AI Developer Düsseldorf', 'TypeScript Developer Düsseldorf', 'Automation Düsseldorf'],
sr: ['AI Developer Diseldorf', 'Fullstack Developer Diseldorf', 'Next.js Programer Diseldorf', 'React Programer Diseldorf'],
},
nearbyAreas: ['Köln', 'Essen', 'Duisburg', 'Wuppertal'],
},
@@ -74,9 +74,9 @@ export const cities: CityData[] = [
sr: 'Frankfurt kao finansijska metropola Nemačke ima posebne zahteve za tehnologiju i automatizaciju. Razvijam AI rešenja i SaaS aplikacije koje ispunjavaju visoke standarde finansijske industrije.',
},
keywords: {
de: ['KI Entwickler Frankfurt', 'SaaS Entwicklung Frankfurt', 'Fullstack Entwickler Frankfurt', 'Fintech Entwickler Frankfurt', 'Automatisierung Frankfurt'],
en: ['AI Developer Frankfurt', 'SaaS Development Frankfurt', 'Fullstack Developer Frankfurt', 'Fintech Developer Frankfurt', 'Automation Frankfurt'],
sr: ['AI Developer Frankfurt', 'SaaS Razvoj Frankfurt'],
de: ['KI Entwickler Frankfurt', 'Fullstack Entwickler Frankfurt', 'Next.js Entwickler Frankfurt', 'React Entwickler Frankfurt', 'Claude AI Frankfurt', 'TypeScript Entwickler Frankfurt', 'Künstliche Intelligenz Frankfurt', 'SaaS Entwicklung Frankfurt', 'Fintech Entwickler Frankfurt', 'Automatisierung Frankfurt'],
en: ['AI Developer Frankfurt', 'Fullstack Developer Frankfurt', 'Next.js Developer Frankfurt', 'React Developer Frankfurt', 'Claude AI Developer Frankfurt', 'TypeScript Developer Frankfurt', 'SaaS Development Frankfurt', 'Fintech Developer Frankfurt', 'Automation Frankfurt'],
sr: ['AI Developer Frankfurt', 'Next.js Programer Frankfurt', 'React Programer Frankfurt', 'Claude AI Frankfurt', 'SaaS Razvoj Frankfurt'],
},
nearbyAreas: ['Wiesbaden', 'Mainz', 'Darmstadt', 'Offenbach'],
},
@@ -94,9 +94,9 @@ export const cities: CityData[] = [
sr: 'Minhen je vodeća tehnološka lokacija u Nemačkoj. Kao remote freelancer, podržavam kompanije iz Minhena u razvoju AI agenata, Voice AI i rešenja za automatizaciju.',
},
keywords: {
de: ['KI Entwickler München', 'AI Agentur München', 'Fullstack Entwickler München', 'SaaS Entwicklung München', 'Automatisierung München'],
en: ['AI Developer Munich', 'AI Agency Munich', 'Fullstack Developer Munich', 'SaaS Development Munich', 'Automation Munich'],
sr: ['AI Developer Minhen', 'Fullstack Developer Minhen'],
de: ['KI Entwickler München', 'AI Agentur München', 'Fullstack Entwickler München', 'Next.js Entwickler München', 'React Entwickler München', 'Claude AI München', 'TypeScript Entwickler München', 'Künstliche Intelligenz München', 'SaaS Entwicklung München', 'Automatisierung München'],
en: ['AI Developer Munich', 'AI Agency Munich', 'Fullstack Developer Munich', 'Next.js Developer Munich', 'React Developer Munich', 'Claude AI Developer Munich', 'TypeScript Developer Munich', 'SaaS Development Munich', 'Automation Munich'],
sr: ['AI Developer Minhen', 'Fullstack Developer Minhen', 'Next.js Programer Minhen', 'React Programer Minhen', 'Claude AI Minhen'],
},
nearbyAreas: ['Augsburg', 'Ingolstadt', 'Regensburg', 'Nürnberg'],
},
@@ -114,9 +114,9 @@ export const cities: CityData[] = [
sr: 'Startap prestonica Nemačke treba inovativne tehnološke partnere. Podržavam berlinske startape i kompanije u razvoju AI rešenja, MVP-ova i skalabilnih SaaS proizvoda.',
},
keywords: {
de: ['KI Entwickler Berlin', 'Startup Entwickler Berlin', 'SaaS Entwicklung Berlin', 'MVP Entwicklung Berlin', 'Automatisierung Berlin'],
en: ['AI Developer Berlin', 'Startup Developer Berlin', 'SaaS Development Berlin', 'MVP Development Berlin', 'Automation Berlin'],
sr: ['AI Developer Berlin', 'Startup Developer Berlin'],
de: ['KI Entwickler Berlin', 'Fullstack Entwickler Berlin', 'Next.js Entwickler Berlin', 'React Entwickler Berlin', 'Claude AI Berlin', 'TypeScript Entwickler Berlin', 'Künstliche Intelligenz Berlin', 'Startup Entwickler Berlin', 'SaaS Entwicklung Berlin', 'MVP Entwicklung Berlin', 'Automatisierung Berlin'],
en: ['AI Developer Berlin', 'Fullstack Developer Berlin', 'Next.js Developer Berlin', 'React Developer Berlin', 'Claude AI Developer Berlin', 'TypeScript Developer Berlin', 'Startup Developer Berlin', 'SaaS Development Berlin', 'MVP Development Berlin', 'Automation Berlin'],
sr: ['AI Developer Berlin', 'Next.js Programer Berlin', 'React Programer Berlin', 'Claude AI Berlin', 'Startup Developer Berlin'],
},
nearbyAreas: ['Potsdam', 'Leipzig', 'Dresden', 'Hamburg'],
},
@@ -134,9 +134,9 @@ export const cities: CityData[] = [
sr: 'Hamburg kao trgovinsko i medijsko središte ima raznovrsne zahteve za digitalna rešenja. Razvijam AI agente i automatizacije za e-commerce, logistiku i medijske kompanije.',
},
keywords: {
de: ['KI Entwickler Hamburg', 'E-Commerce Entwickler Hamburg', 'Fullstack Entwickler Hamburg', 'Automatisierung Hamburg', 'SaaS Hamburg'],
en: ['AI Developer Hamburg', 'E-Commerce Developer Hamburg', 'Fullstack Developer Hamburg', 'Automation Hamburg', 'SaaS Hamburg'],
sr: ['AI Developer Hamburg', 'E-Commerce Developer Hamburg'],
de: ['KI Entwickler Hamburg', 'E-Commerce Entwickler Hamburg', 'Next.js Entwickler Hamburg', 'React Entwickler Hamburg', 'Claude AI Hamburg', 'TypeScript Entwickler Hamburg', 'Künstliche Intelligenz Hamburg', 'Fullstack Entwickler Hamburg', 'Automatisierung Hamburg', 'SaaS Hamburg'],
en: ['AI Developer Hamburg', 'E-Commerce Developer Hamburg', 'Next.js Developer Hamburg', 'React Developer Hamburg', 'Claude AI Developer Hamburg', 'TypeScript Developer Hamburg', 'Fullstack Developer Hamburg', 'Automation Hamburg', 'SaaS Hamburg'],
sr: ['AI Developer Hamburg', 'E-Commerce Developer Hamburg', 'Next.js Programer Hamburg', 'React Programer Hamburg', 'Claude AI Hamburg'],
},
nearbyAreas: ['Bremen', 'Lübeck', 'Kiel', 'Hannover'],
},
@@ -154,9 +154,9 @@ export const cities: CityData[] = [
sr: 'Bon kao bivši federalni glavni grad i sedište mnogih međunarodnih organizacija treba profesionalna IT rešenja. Nudim AI razvoj i automatizaciju za kompanije iz Bona.',
},
keywords: {
de: ['KI Entwickler Bonn', 'Fullstack Entwickler Bonn', 'Webentwickler Bonn', 'Automatisierung Bonn', 'IT Freelancer Bonn'],
en: ['AI Developer Bonn', 'Fullstack Developer Bonn', 'Web Developer Bonn', 'Automation Bonn', 'IT Freelancer Bonn'],
sr: ['AI Developer Bon', 'Fullstack Developer Bon'],
de: ['KI Entwickler Bonn', 'Fullstack Entwickler Bonn', 'Next.js Entwickler Bonn', 'React Entwickler Bonn', 'Claude AI Bonn', 'TypeScript Entwickler Bonn', 'Künstliche Intelligenz Bonn', 'Webentwickler Bonn', 'Automatisierung Bonn', 'IT Freelancer Bonn'],
en: ['AI Developer Bonn', 'Fullstack Developer Bonn', 'Next.js Developer Bonn', 'React Developer Bonn', 'Claude AI Developer Bonn', 'TypeScript Developer Bonn', 'Web Developer Bonn', 'Automation Bonn', 'IT Freelancer Bonn'],
sr: ['AI Developer Bon', 'Fullstack Developer Bon', 'Next.js Programer Bon', 'React Programer Bon', 'Claude AI Bon'],
},
nearbyAreas: ['Köln', 'Koblenz', 'Siegburg', 'Bad Godesberg'],
},
@@ -174,9 +174,9 @@ export const cities: CityData[] = [
sr: 'Štutgart je srce nemačke automobilske industrije i Industrije 4.0. Razvijam AI rešenja i automatizacije za mašinstvo, automotive i proizvodne kompanije u regionu.',
},
keywords: {
de: ['KI Entwickler Stuttgart', 'Industrie 4.0 Stuttgart', 'Automatisierung Stuttgart', 'KI Automotive Stuttgart', 'Machine Learning Stuttgart', 'IoT Entwickler Stuttgart'],
en: ['AI Developer Stuttgart', 'Industry 4.0 Stuttgart', 'Automation Stuttgart', 'AI Automotive Stuttgart', 'Machine Learning Stuttgart', 'IoT Developer Stuttgart'],
sr: ['AI Developer Štutgart', 'Industrie 4.0 Štutgart', 'Automatizacija Štutgart'],
de: ['KI Entwickler Stuttgart', 'Industrie 4.0 Stuttgart', 'Next.js Entwickler Stuttgart', 'React Entwickler Stuttgart', 'Claude AI Stuttgart', 'TypeScript Entwickler Stuttgart', 'Künstliche Intelligenz Stuttgart', 'Automatisierung Stuttgart', 'KI Automotive Stuttgart', 'Machine Learning Stuttgart', 'IoT Entwickler Stuttgart'],
en: ['AI Developer Stuttgart', 'Industry 4.0 Stuttgart', 'Next.js Developer Stuttgart', 'React Developer Stuttgart', 'Claude AI Developer Stuttgart', 'TypeScript Developer Stuttgart', 'Automation Stuttgart', 'AI Automotive Stuttgart', 'Machine Learning Stuttgart', 'IoT Developer Stuttgart'],
sr: ['AI Developer Štutgart', 'Industrie 4.0 Štutgart', 'Next.js Programer Štutgart', 'React Programer Štutgart', 'Claude AI Štutgart', 'Automatizacija Štutgart'],
},
nearbyAreas: ['Esslingen', 'Ludwigsburg', 'Böblingen', 'Sindelfingen', 'Karlsruhe'],
},
@@ -194,9 +194,9 @@ export const cities: CityData[] = [
sr: 'Esen u srcu Rurske oblasti transformiše se od industrijskog u digitalni centar. Podržavam kompanije iz Esena u digitalnoj transformaciji sa AI agentima i automatizacijom procesa.',
},
keywords: {
de: ['KI Entwickler Essen', 'Automatisierung Essen', 'Digitalisierung Essen', 'Fullstack Entwickler Essen', 'n8n Entwickler Essen', 'Prozessautomatisierung Essen'],
en: ['AI Developer Essen', 'Automation Essen', 'Digitalization Essen', 'Fullstack Developer Essen', 'n8n Developer Essen', 'Process Automation Essen'],
sr: ['AI Developer Esen', 'Automatizacija Esen', 'Digitalizacija Esen'],
de: ['KI Entwickler Essen', 'Automatisierung Essen', 'Next.js Entwickler Essen', 'React Entwickler Essen', 'Claude AI Essen', 'TypeScript Entwickler Essen', 'Künstliche Intelligenz Essen', 'Digitalisierung Essen', 'Fullstack Entwickler Essen', 'n8n Entwickler Essen', 'Prozessautomatisierung Essen'],
en: ['AI Developer Essen', 'Automation Essen', 'Next.js Developer Essen', 'React Developer Essen', 'Claude AI Developer Essen', 'TypeScript Developer Essen', 'Digitalization Essen', 'Fullstack Developer Essen', 'n8n Developer Essen', 'Process Automation Essen'],
sr: ['AI Developer Esen', 'Automatizacija Esen', 'Next.js Programer Esen', 'React Programer Esen', 'Claude AI Esen', 'Digitalizacija Esen'],
},
nearbyAreas: ['Dortmund', 'Duisburg', 'Bochum', 'Gelsenkirchen', 'Düsseldorf'],
},
@@ -214,9 +214,9 @@ export const cities: CityData[] = [
sr: 'Dortmund se razvija u vodeći tech hub u Rurskoj oblasti. Sa TU Dortmund i brojnim startapima, nudim AI razvoj, SaaS rešenja i automatizaciju za inovativne kompanije.',
},
keywords: {
de: ['KI Entwickler Dortmund', 'Startup Entwickler Dortmund', 'SaaS Entwicklung Dortmund', 'Fullstack Entwickler Dortmund', 'Automatisierung Dortmund', 'Tech Freelancer Dortmund'],
en: ['AI Developer Dortmund', 'Startup Developer Dortmund', 'SaaS Development Dortmund', 'Fullstack Developer Dortmund', 'Automation Dortmund', 'Tech Freelancer Dortmund'],
sr: ['AI Developer Dortmund', 'Startup Developer Dortmund', 'SaaS Razvoj Dortmund'],
de: ['KI Entwickler Dortmund', 'Startup Entwickler Dortmund', 'Next.js Entwickler Dortmund', 'React Entwickler Dortmund', 'Claude AI Dortmund', 'TypeScript Entwickler Dortmund', 'Künstliche Intelligenz Dortmund', 'SaaS Entwicklung Dortmund', 'Fullstack Entwickler Dortmund', 'Automatisierung Dortmund', 'Tech Freelancer Dortmund'],
en: ['AI Developer Dortmund', 'Startup Developer Dortmund', 'Next.js Developer Dortmund', 'React Developer Dortmund', 'Claude AI Developer Dortmund', 'TypeScript Developer Dortmund', 'SaaS Development Dortmund', 'Fullstack Developer Dortmund', 'Automation Dortmund', 'Tech Freelancer Dortmund'],
sr: ['AI Developer Dortmund', 'Startup Developer Dortmund', 'Next.js Programer Dortmund', 'React Programer Dortmund', 'Claude AI Dortmund', 'SaaS Razvoj Dortmund'],
},
nearbyAreas: ['Essen', 'Bochum', 'Hagen', 'Münster', 'Köln'],
},
@@ -234,9 +234,9 @@ export const cities: CityData[] = [
sr: 'Lajpcig je rastući tech centar istočne Nemačke. Podržavam startape i etablirane kompanije iz Lajpciga sa AI razvojem, Voice AI i modernim rešenjima za automatizaciju.',
},
keywords: {
de: ['KI Entwickler Leipzig', 'Startup Entwickler Leipzig', 'Voice AI Leipzig', 'Fullstack Entwickler Leipzig', 'Automatisierung Leipzig', 'n8n Entwickler Leipzig'],
en: ['AI Developer Leipzig', 'Startup Developer Leipzig', 'Voice AI Leipzig', 'Fullstack Developer Leipzig', 'Automation Leipzig', 'n8n Developer Leipzig'],
sr: ['AI Developer Lajpcig', 'Startup Developer Lajpcig', 'Voice AI Lajpcig'],
de: ['KI Entwickler Leipzig', 'Startup Entwickler Leipzig', 'Next.js Entwickler Leipzig', 'React Entwickler Leipzig', 'Claude AI Leipzig', 'TypeScript Entwickler Leipzig', 'Künstliche Intelligenz Leipzig', 'Voice AI Leipzig', 'Fullstack Entwickler Leipzig', 'Automatisierung Leipzig', 'n8n Entwickler Leipzig'],
en: ['AI Developer Leipzig', 'Startup Developer Leipzig', 'Next.js Developer Leipzig', 'React Developer Leipzig', 'Claude AI Developer Leipzig', 'TypeScript Developer Leipzig', 'Voice AI Leipzig', 'Fullstack Developer Leipzig', 'Automation Leipzig', 'n8n Developer Leipzig'],
sr: ['AI Developer Lajpcig', 'Startup Developer Lajpcig', 'Next.js Programer Lajpcig', 'React Programer Lajpcig', 'Claude AI Lajpcig', 'Voice AI Lajpcig'],
},
nearbyAreas: ['Dresden', 'Halle', 'Berlin', 'Chemnitz', 'Magdeburg'],
},
@@ -255,9 +255,9 @@ export const cities: CityData[] = [
sr: 'Beč kao glavni grad Austrije je značajan ekonomski i tehnološki centar u DACH regionu. Nudim razvoj AI i rešenja za automatizaciju za bečke kompanije.',
},
keywords: {
de: ['KI Entwickler Wien', 'AI Agentur Wien', 'Fullstack Entwickler Wien', 'Automatisierung Wien', 'n8n Entwickler Österreich', 'Voice AI Wien'],
en: ['AI Developer Vienna', 'AI Agency Vienna', 'Fullstack Developer Vienna', 'Automation Vienna', 'n8n Developer Austria'],
sr: ['AI Developer Beč', 'Fullstack Developer Austrija'],
de: ['KI Entwickler Wien', 'AI Agentur Wien', 'Next.js Entwickler Wien', 'React Entwickler Wien', 'Claude AI Wien', 'TypeScript Entwickler Wien', 'Künstliche Intelligenz Wien', 'Fullstack Entwickler Wien', 'Automatisierung Wien', 'n8n Entwickler Österreich', 'Voice AI Wien'],
en: ['AI Developer Vienna', 'AI Agency Vienna', 'Next.js Developer Vienna', 'React Developer Vienna', 'Claude AI Developer Vienna', 'TypeScript Developer Vienna', 'Fullstack Developer Vienna', 'Automation Vienna', 'n8n Developer Austria'],
sr: ['AI Developer Beč', 'Fullstack Developer Austrija', 'Next.js Programer Beč', 'React Programer Beč', 'Claude AI Beč'],
},
nearbyAreas: ['Graz', 'Linz', 'Salzburg', 'Bratislava'],
},
@@ -275,9 +275,9 @@ export const cities: CityData[] = [
sr: 'Grac je drugi najveći grad Austrije i važna lokacija za tehnologiju i inovacije. Podržavam kompanije iz Graca sa AI rešenjima i modernom automatizacijom procesa.',
},
keywords: {
de: ['KI Entwickler Graz', 'Fullstack Entwickler Graz', 'Automatisierung Graz', 'SaaS Entwicklung Graz', 'Tech Freelancer Graz'],
en: ['AI Developer Graz', 'Fullstack Developer Graz', 'Automation Graz', 'SaaS Development Graz'],
sr: ['AI Developer Grac', 'Fullstack Developer Grac'],
de: ['KI Entwickler Graz', 'Fullstack Entwickler Graz', 'Next.js Entwickler Graz', 'React Entwickler Graz', 'Claude AI Graz', 'TypeScript Entwickler Graz', 'Künstliche Intelligenz Graz', 'Automatisierung Graz', 'SaaS Entwicklung Graz', 'Tech Freelancer Graz'],
en: ['AI Developer Graz', 'Fullstack Developer Graz', 'Next.js Developer Graz', 'React Developer Graz', 'Claude AI Developer Graz', 'TypeScript Developer Graz', 'Automation Graz', 'SaaS Development Graz'],
sr: ['AI Developer Grac', 'Fullstack Developer Grac', 'Next.js Programer Grac', 'React Programer Grac', 'Claude AI Grac'],
},
nearbyAreas: ['Wien', 'Klagenfurt', 'Maribor', 'Ljubljana'],
},
@@ -296,9 +296,9 @@ export const cities: CityData[] = [
sr: 'Cirih je finansijski i ekonomski centar Švajcarske. Nudim razvoj AI i rešenja za automatizaciju za švajcarske kompanije sa najvišim standardima kvaliteta.',
},
keywords: {
de: ['KI Entwickler Zürich', 'AI Agentur Zürich', 'Fullstack Entwickler Schweiz', 'Automatisierung Zürich', 'Fintech Entwickler Zürich', 'SaaS Entwicklung Schweiz'],
en: ['AI Developer Zurich', 'AI Agency Zurich', 'Fullstack Developer Switzerland', 'Automation Zurich', 'Fintech Developer Zurich'],
sr: ['AI Developer Cirih', 'Fullstack Developer Švajcarska'],
de: ['KI Entwickler Zürich', 'AI Agentur Zürich', 'Next.js Entwickler Zürich', 'React Entwickler Zürich', 'Claude AI Zürich', 'TypeScript Entwickler Zürich', 'Künstliche Intelligenz Zürich', 'Fullstack Entwickler Schweiz', 'Automatisierung Zürich', 'Fintech Entwickler Zürich', 'SaaS Entwicklung Schweiz'],
en: ['AI Developer Zurich', 'AI Agency Zurich', 'Next.js Developer Zurich', 'React Developer Zurich', 'Claude AI Developer Zurich', 'TypeScript Developer Zurich', 'Fullstack Developer Switzerland', 'Automation Zurich', 'Fintech Developer Zurich'],
sr: ['AI Developer Cirih', 'Fullstack Developer Švajcarska', 'Next.js Programer Cirih', 'React Programer Cirih', 'Claude AI Cirih'],
},
nearbyAreas: ['Basel', 'Bern', 'Luzern', 'Winterthur'],
},
@@ -316,9 +316,9 @@ export const cities: CityData[] = [
sr: 'Bazel je važna farmaceutska i life sciences lokacija u Švajcarskoj. Podržavam kompanije iz Bazela sa AI rešenjima za Industriju 4.0 i automatizaciju procesa.',
},
keywords: {
de: ['KI Entwickler Basel', 'Pharma IT Basel', 'Fullstack Entwickler Basel', 'Automatisierung Basel', 'Life Sciences IT Basel'],
en: ['AI Developer Basel', 'Pharma IT Basel', 'Fullstack Developer Basel', 'Automation Basel', 'Life Sciences IT Basel'],
sr: ['AI Developer Bazel', 'Fullstack Developer Bazel'],
de: ['KI Entwickler Basel', 'Pharma IT Basel', 'Next.js Entwickler Basel', 'React Entwickler Basel', 'Claude AI Basel', 'TypeScript Entwickler Basel', 'Künstliche Intelligenz Basel', 'Fullstack Entwickler Basel', 'Automatisierung Basel', 'Life Sciences IT Basel'],
en: ['AI Developer Basel', 'Pharma IT Basel', 'Next.js Developer Basel', 'React Developer Basel', 'Claude AI Developer Basel', 'TypeScript Developer Basel', 'Fullstack Developer Basel', 'Automation Basel', 'Life Sciences IT Basel'],
sr: ['AI Developer Bazel', 'Fullstack Developer Bazel', 'Next.js Programer Bazel', 'React Programer Bazel', 'Claude AI Bazel'],
},
nearbyAreas: ['Zürich', 'Bern', 'Freiburg', 'Strasbourg'],
},
@@ -337,9 +337,9 @@ export const cities: CityData[] = [
sr: 'London je jedan od vodećih svetskih finansijskih centara i tech habova. Nudim remote AI razvoj i rešenja za automatizaciju za londonske kompanije sa nemačkim inženjerskim kvalitetom.',
},
keywords: {
de: ['KI Entwickler London', 'Remote Developer UK', 'AI Freelancer London', 'Automatisierung London'],
en: ['AI Developer London', 'Remote Developer UK', 'AI Freelancer London', 'Process Automation London', 'n8n Developer London', 'Voice AI London', 'Python Developer London', 'React Developer UK'],
sr: ['AI Developer London', 'Remote Programer UK'],
de: ['KI Entwickler London', 'Remote Developer UK', 'Next.js Entwickler London', 'React Entwickler London', 'Claude AI London', 'TypeScript Entwickler London', 'Künstliche Intelligenz London', 'AI Freelancer London', 'Automatisierung London'],
en: ['AI Developer London', 'Remote Developer UK', 'Next.js Developer London', 'React Developer London', 'Claude AI Developer London', 'TypeScript Developer London', 'AI Freelancer London', 'Process Automation London', 'n8n Developer London', 'Voice AI London', 'Python Developer London', 'React Developer UK'],
sr: ['AI Developer London', 'Remote Programer UK', 'Next.js Programer London', 'React Programer London', 'Claude AI London'],
},
nearbyAreas: ['Manchester', 'Birmingham', 'Cambridge', 'Oxford'],
},
@@ -358,9 +358,9 @@ export const cities: CityData[] = [
sr: 'Njujork je ekonomsko srce SAD-a. Podržavam njujorške startape i preduzeća na daljinu sa AI agentima, Voice AI i rešenjima za automatizaciju procesa.',
},
keywords: {
de: ['KI Entwickler New York', 'Remote Developer USA', 'AI Freelancer NYC', 'Automatisierung New York'],
en: ['AI Developer New York', 'Remote Developer NYC', 'AI Freelancer New York', 'Process Automation NYC', 'n8n Developer New York', 'Voice AI NYC', 'Python Developer New York', 'Fullstack Developer NYC'],
sr: ['AI Developer Njujork', 'Remote Programer SAD'],
de: ['KI Entwickler New York', 'Remote Developer USA', 'Next.js Entwickler New York', 'React Entwickler New York', 'Claude AI New York', 'TypeScript Entwickler New York', 'Künstliche Intelligenz New York', 'AI Freelancer NYC', 'Automatisierung New York'],
en: ['AI Developer New York', 'Remote Developer NYC', 'Next.js Developer New York', 'React Developer New York', 'Claude AI Developer New York', 'TypeScript Developer New York', 'AI Freelancer New York', 'Process Automation NYC', 'n8n Developer New York', 'Voice AI NYC', 'Python Developer New York', 'Fullstack Developer NYC'],
sr: ['AI Developer Njujork', 'Remote Programer SAD', 'Next.js Programer Njujork', 'React Programer Njujork', 'Claude AI Njujork'],
},
nearbyAreas: ['Brooklyn', 'Manhattan', 'New Jersey', 'Connecticut'],
},
@@ -378,9 +378,9 @@ export const cities: CityData[] = [
sr: 'Los Anđeles je vodeći centar za tehnologiju, zabavu i inovacije. Nudim remote AI razvoj i rešenja za automatizaciju za kompanije iz LA.',
},
keywords: {
de: ['KI Entwickler Los Angeles', 'Remote Developer Kalifornien', 'AI Freelancer LA', 'Tech Entwickler LA'],
en: ['AI Developer Los Angeles', 'Remote Developer California', 'AI Freelancer LA', 'Process Automation Los Angeles', 'Tech Developer LA', 'Python Developer Los Angeles', 'SaaS Developer California'],
sr: ['AI Developer Los Anđeles', 'Remote Programer Kalifornija'],
de: ['KI Entwickler Los Angeles', 'Remote Developer Kalifornien', 'Next.js Entwickler Los Angeles', 'React Entwickler Los Angeles', 'Claude AI Los Angeles', 'TypeScript Entwickler Los Angeles', 'Künstliche Intelligenz Los Angeles', 'AI Freelancer LA', 'Tech Entwickler LA'],
en: ['AI Developer Los Angeles', 'Remote Developer California', 'Next.js Developer Los Angeles', 'React Developer Los Angeles', 'Claude AI Developer Los Angeles', 'TypeScript Developer Los Angeles', 'AI Freelancer LA', 'Process Automation Los Angeles', 'Tech Developer LA', 'Python Developer Los Angeles', 'SaaS Developer California'],
sr: ['AI Developer Los Anđeles', 'Remote Programer Kalifornija', 'Next.js Programer Los Anđeles', 'React Programer Los Anđeles', 'Claude AI Los Anđeles'],
},
nearbyAreas: ['San Francisco', 'San Diego', 'Silicon Valley', 'Orange County'],
},
@@ -398,9 +398,9 @@ export const cities: CityData[] = [
sr: 'Majami se razvija kao rastući tech hab u SAD sa fokusom na fintech i kripto. Podržavam kompanije iz Majamija sa AI rešenjima i modernom automatizacijom procesa.',
},
keywords: {
de: ['KI Entwickler Miami', 'Remote Developer Florida', 'AI Freelancer Miami', 'Fintech Entwickler Miami'],
en: ['AI Developer Miami', 'Remote Developer Florida', 'AI Freelancer Miami', 'Process Automation Miami', 'Fintech Developer Miami', 'Python Developer Miami', 'Startup Developer Miami'],
sr: ['AI Developer Majami', 'Remote Programer Florida'],
de: ['KI Entwickler Miami', 'Remote Developer Florida', 'Next.js Entwickler Miami', 'React Entwickler Miami', 'Claude AI Miami', 'TypeScript Entwickler Miami', 'Künstliche Intelligenz Miami', 'AI Freelancer Miami', 'Fintech Entwickler Miami'],
en: ['AI Developer Miami', 'Remote Developer Florida', 'Next.js Developer Miami', 'React Developer Miami', 'Claude AI Developer Miami', 'TypeScript Developer Miami', 'AI Freelancer Miami', 'Process Automation Miami', 'Fintech Developer Miami', 'Python Developer Miami', 'Startup Developer Miami'],
sr: ['AI Developer Majami', 'Remote Programer Florida', 'Next.js Programer Majami', 'React Programer Majami', 'Claude AI Majami'],
},
nearbyAreas: ['Fort Lauderdale', 'Tampa', 'Orlando', 'West Palm Beach'],
},
@@ -418,9 +418,9 @@ export const cities: CityData[] = [
sr: 'San Francisko i Silikonska dolina su globalni centar za tehnologiju i inovacije. Nudim remote razvojne usluge za startape i tech kompanije iz SF.',
},
keywords: {
de: ['KI Entwickler San Francisco', 'Remote Developer Silicon Valley', 'AI Freelancer SF', 'Startup Entwickler Bay Area'],
en: ['AI Developer San Francisco', 'Remote Developer Silicon Valley', 'AI Freelancer SF', 'Process Automation San Francisco', 'Startup Developer Bay Area', 'Python Developer SF', 'SaaS Developer Silicon Valley'],
sr: ['AI Developer San Francisko', 'Remote Programer Silikonska Dolina'],
de: ['KI Entwickler San Francisco', 'Remote Developer Silicon Valley', 'Next.js Entwickler San Francisco', 'React Entwickler San Francisco', 'Claude AI San Francisco', 'TypeScript Entwickler San Francisco', 'Künstliche Intelligenz San Francisco', 'AI Freelancer SF', 'Startup Entwickler Bay Area'],
en: ['AI Developer San Francisco', 'Remote Developer Silicon Valley', 'Next.js Developer San Francisco', 'React Developer San Francisco', 'Claude AI Developer San Francisco', 'TypeScript Developer San Francisco', 'AI Freelancer SF', 'Process Automation San Francisco', 'Startup Developer Bay Area', 'Python Developer SF', 'SaaS Developer Silicon Valley'],
sr: ['AI Developer San Francisko', 'Remote Programer Silikonska Dolina', 'Next.js Programer San Francisko', 'React Programer San Francisko', 'Claude AI San Francisko'],
},
nearbyAreas: ['Silicon Valley', 'Palo Alto', 'Oakland', 'San Jose'],
},
@@ -438,9 +438,9 @@ export const cities: CityData[] = [
sr: 'Čikago je značajan ekonomski centar na Srednjem zapadu SAD-a. Podržavam kompanije iz Čikaga na daljinu sa AI razvojem i rešenjima za automatizaciju.',
},
keywords: {
de: ['KI Entwickler Chicago', 'Remote Developer Midwest', 'AI Freelancer Chicago', 'Automatisierung Chicago'],
en: ['AI Developer Chicago', 'Remote Developer Midwest', 'AI Freelancer Chicago', 'Process Automation Chicago', 'Python Developer Chicago', 'Fullstack Developer Chicago'],
sr: ['AI Developer Čikago', 'Remote Programer Midwest'],
de: ['KI Entwickler Chicago', 'Remote Developer Midwest', 'Next.js Entwickler Chicago', 'React Entwickler Chicago', 'Claude AI Chicago', 'TypeScript Entwickler Chicago', 'Künstliche Intelligenz Chicago', 'AI Freelancer Chicago', 'Automatisierung Chicago'],
en: ['AI Developer Chicago', 'Remote Developer Midwest', 'Next.js Developer Chicago', 'React Developer Chicago', 'Claude AI Developer Chicago', 'TypeScript Developer Chicago', 'AI Freelancer Chicago', 'Process Automation Chicago', 'Python Developer Chicago', 'Fullstack Developer Chicago'],
sr: ['AI Developer Čikago', 'Remote Programer Midwest', 'Next.js Programer Čikago', 'React Programer Čikago', 'Claude AI Čikago'],
},
nearbyAreas: ['Milwaukee', 'Indianapolis', 'Detroit', 'Minneapolis'],
},
@@ -459,9 +459,9 @@ export const cities: CityData[] = [
sr: 'Beograd je ekonomski i tehnološki centar Srbije. Nudim razvoj AI i rešenja za automatizaciju za srpske kompanije sa nemačkom preciznošću i kvalitetom.',
},
keywords: {
de: ['KI Entwickler Belgrad', 'AI Entwicklung Serbien', 'Fullstack Entwickler Belgrad', 'Automatisierung Serbien'],
en: ['AI Developer Belgrade', 'AI Development Serbia', 'Fullstack Developer Belgrade', 'Automation Serbia'],
sr: ['AI Developer Beograd', 'Softverski Razvoj Beograd', 'Fullstack Programer Beograd', 'Automatizacija Procesa Srbija', 'n8n Automatizacija Beograd', 'Voice AI Beograd'],
de: ['KI Entwickler Belgrad', 'AI Entwicklung Serbien', 'Next.js Entwickler Belgrad', 'React Entwickler Belgrad', 'Claude AI Belgrad', 'TypeScript Entwickler Belgrad', 'Künstliche Intelligenz Belgrad', 'Fullstack Entwickler Belgrad', 'Automatisierung Serbien'],
en: ['AI Developer Belgrade', 'AI Development Serbia', 'Next.js Developer Belgrade', 'React Developer Belgrade', 'Claude AI Developer Belgrade', 'TypeScript Developer Belgrade', 'Fullstack Developer Belgrade', 'Automation Serbia'],
sr: ['AI Developer Beograd', 'Softverski Razvoj Beograd', 'Next.js Programer Beograd', 'React Programer Beograd', 'Claude AI Beograd', 'Fullstack Programer Beograd', 'Automatizacija Procesa Srbija', 'n8n Automatizacija Beograd', 'Voice AI Beograd'],
},
nearbyAreas: ['Novi Sad', 'Niš', 'Kragujevac', 'Subotica'],
},
@@ -479,9 +479,9 @@ export const cities: CityData[] = [
sr: 'Novi Sad je rastući IT centar Srbije i Evropska prestonica kulture 2022. Podržavam rastuću tech scenu sa AI rešenjima i modernom automatizacijom procesa.',
},
keywords: {
de: ['KI Entwickler Novi Sad', 'IT Freelancer Serbien', 'Fullstack Entwickler Novi Sad', 'Automatisierung Vojvodina'],
en: ['AI Developer Novi Sad', 'IT Freelancer Serbia', 'Fullstack Developer Novi Sad', 'Automation Vojvodina'],
sr: ['AI Developer Novi Sad', 'Softverski Razvoj Novi Sad', 'Fullstack Programer Novi Sad', 'IT Freelancer Novi Sad', 'Web Razvoj Novi Sad', 'Startup Developer Novi Sad'],
de: ['KI Entwickler Novi Sad', 'IT Freelancer Serbien', 'Next.js Entwickler Novi Sad', 'React Entwickler Novi Sad', 'Claude AI Novi Sad', 'TypeScript Entwickler Novi Sad', 'Künstliche Intelligenz Novi Sad', 'Fullstack Entwickler Novi Sad', 'Automatisierung Vojvodina'],
en: ['AI Developer Novi Sad', 'IT Freelancer Serbia', 'Next.js Developer Novi Sad', 'React Developer Novi Sad', 'Claude AI Developer Novi Sad', 'TypeScript Developer Novi Sad', 'Fullstack Developer Novi Sad', 'Automation Vojvodina'],
sr: ['AI Developer Novi Sad', 'Softverski Razvoj Novi Sad', 'Next.js Programer Novi Sad', 'React Programer Novi Sad', 'Claude AI Novi Sad', 'Fullstack Programer Novi Sad', 'IT Freelancer Novi Sad', 'Web Razvoj Novi Sad', 'Startup Developer Novi Sad'],
},
nearbyAreas: ['Beograd', 'Subotica', 'Zrenjanin', 'Sombor'],
},
@@ -499,9 +499,9 @@ export const cities: CityData[] = [
sr: 'Niš je treći najveći grad Srbije i rastući IT centar na jugu zemlje. Nudim AI razvoj i rešenja za automatizaciju za kompanije iz Niša i južne Srbije.',
},
keywords: {
de: ['KI Entwickler Niš', 'IT Freelancer Südserbien', 'Fullstack Entwickler Niš', 'Automatisierung Niš'],
en: ['AI Developer Niš', 'IT Freelancer South Serbia', 'Fullstack Developer Niš', 'Automation Niš'],
sr: ['AI Developer Niš', 'Softverski Razvoj Niš', 'Fullstack Programer Niš', 'IT Freelancer Niš', 'Web Razvoj Niš', 'Python Programer Niš'],
de: ['KI Entwickler Niš', 'IT Freelancer Südserbien', 'Next.js Entwickler Niš', 'React Entwickler Niš', 'Claude AI Niš', 'TypeScript Entwickler Niš', 'Künstliche Intelligenz Niš', 'Fullstack Entwickler Niš', 'Automatisierung Niš'],
en: ['AI Developer Niš', 'IT Freelancer South Serbia', 'Next.js Developer Niš', 'React Developer Niš', 'Claude AI Developer Niš', 'TypeScript Developer Niš', 'Fullstack Developer Niš', 'Automation Niš'],
sr: ['AI Developer Niš', 'Softverski Razvoj Niš', 'Next.js Programer Niš', 'React Programer Niš', 'Claude AI Niš', 'Fullstack Programer Niš', 'IT Freelancer Niš', 'Web Razvoj Niš', 'Python Programer Niš'],
},
nearbyAreas: ['Beograd', 'Kragujevac', 'Leskovac', 'Vranje'],
},
@@ -519,9 +519,9 @@ export const cities: CityData[] = [
sr: 'Kragujevac je industrijski centar centralne Srbije sa jakom automobilskom industrijom. Podržavam kompanije iz Kragujevca sa modernim AI i rešenjima za automatizaciju.',
},
keywords: {
de: ['KI Entwickler Kragujevac', 'IT Freelancer Zentralserbien', 'Fullstack Entwickler Kragujevac', 'Automatisierung Kragujevac'],
en: ['AI Developer Kragujevac', 'IT Freelancer Central Serbia', 'Fullstack Developer Kragujevac', 'Automation Kragujevac'],
sr: ['AI Developer Kragujevac', 'Softverski Razvoj Kragujevac', 'Fullstack Programer Kragujevac', 'IT Freelancer Kragujevac', 'Web Razvoj Kragujevac', 'Automatizacija Procesa Kragujevac'],
de: ['KI Entwickler Kragujevac', 'IT Freelancer Zentralserbien', 'Next.js Entwickler Kragujevac', 'React Entwickler Kragujevac', 'Claude AI Kragujevac', 'TypeScript Entwickler Kragujevac', 'Künstliche Intelligenz Kragujevac', 'Fullstack Entwickler Kragujevac', 'Automatisierung Kragujevac'],
en: ['AI Developer Kragujevac', 'IT Freelancer Central Serbia', 'Next.js Developer Kragujevac', 'React Developer Kragujevac', 'Claude AI Developer Kragujevac', 'TypeScript Developer Kragujevac', 'Fullstack Developer Kragujevac', 'Automation Kragujevac'],
sr: ['AI Developer Kragujevac', 'Softverski Razvoj Kragujevac', 'Next.js Programer Kragujevac', 'React Programer Kragujevac', 'Claude AI Kragujevac', 'Fullstack Programer Kragujevac', 'IT Freelancer Kragujevac', 'Web Razvoj Kragujevac', 'Automatizacija Procesa Kragujevac'],
},
nearbyAreas: ['Beograd', 'Niš', 'Čačak', 'Kraljevo'],
},
@@ -539,9 +539,9 @@ export const cities: CityData[] = [
sr: 'Subotica se nalazi na severu Srbije blizu mađarske granice i poznata je po svojoj multikulturalnoj istoriji. Nudim AI razvoj i automatizaciju za rastuću tech scenu.',
},
keywords: {
de: ['KI Entwickler Subotica', 'IT Freelancer Nordserbien', 'Fullstack Entwickler Subotica', 'Automatisierung Subotica'],
en: ['AI Developer Subotica', 'IT Freelancer North Serbia', 'Fullstack Developer Subotica', 'Automation Subotica'],
sr: ['AI Developer Subotica', 'Softverski Razvoj Subotica', 'Fullstack Programer Subotica', 'IT Freelancer Subotica', 'Web Razvoj Subotica', 'Remote Programer Subotica'],
de: ['KI Entwickler Subotica', 'IT Freelancer Nordserbien', 'Next.js Entwickler Subotica', 'React Entwickler Subotica', 'Claude AI Subotica', 'TypeScript Entwickler Subotica', 'Künstliche Intelligenz Subotica', 'Fullstack Entwickler Subotica', 'Automatisierung Subotica'],
en: ['AI Developer Subotica', 'IT Freelancer North Serbia', 'Next.js Developer Subotica', 'React Developer Subotica', 'Claude AI Developer Subotica', 'TypeScript Developer Subotica', 'Fullstack Developer Subotica', 'Automation Subotica'],
sr: ['AI Developer Subotica', 'Softverski Razvoj Subotica', 'Next.js Programer Subotica', 'React Programer Subotica', 'Claude AI Subotica', 'Fullstack Programer Subotica', 'IT Freelancer Subotica', 'Web Razvoj Subotica', 'Remote Programer Subotica'],
},
nearbyAreas: ['Novi Sad', 'Sombor', 'Szeged', 'Beograd'],
},
+30
View File
@@ -22,6 +22,11 @@ export interface ServiceData {
sr: string[];
};
technologies: string[];
seoKeywords?: {
de: string[];
en: string[];
sr: string[];
};
useCases: {
de: string[];
en: string[];
@@ -72,6 +77,11 @@ export const services: ServiceData[] = [
],
},
technologies: ['OpenAI API', 'Claude API', 'LangChain', 'LlamaIndex', 'Python', 'FastAPI'],
seoKeywords: {
de: ['KI Entwickler beauftragen', 'Claude AI Integration', 'KI-Agenten Entwicklung', 'LLM Integration Unternehmen', 'Künstliche Intelligenz Freelancer', 'AI Agentur Köln'],
en: ['Hire AI Developer', 'Claude AI Integration Expert', 'AI Agent Development', 'LLM Integration Services', 'AI Consultant', 'AI Developer for Hire'],
sr: ['AI Razvoj Srbija', 'Claude AI Integracija', 'Razvoj AI Agenata', 'LLM Integracija', 'AI Konsultant'],
},
useCases: {
de: [
'Kundenservice-Automatisierung',
@@ -135,6 +145,11 @@ export const services: ServiceData[] = [
],
},
technologies: ['Vapi', 'Twilio', 'ElevenLabs', 'Whisper', 'Python', 'WebRTC'],
seoKeywords: {
de: ['Voice AI Entwickler', 'Sprachassistent entwickeln', 'Telefon-Bot Entwicklung', 'Voice Bot Agentur', 'KI Sprachassistent'],
en: ['Hire Voice AI Developer', 'Voice Bot Development', 'Phone Bot Developer', 'Voice Assistant Expert', 'AI Voice Agent'],
sr: ['Voice AI Razvoj', 'Glasovni Asistent Razvoj', 'Telefonski Bot Srbija'],
},
useCases: {
de: [
'Automatisierte Terminbuchung',
@@ -198,6 +213,11 @@ export const services: ServiceData[] = [
],
},
technologies: ['n8n', 'Make', 'Zapier', 'Python', 'Playwright', 'Puppeteer'],
seoKeywords: {
de: ['Prozessautomatisierung Freelancer', 'n8n Experte beauftragen', 'Workflow Automatisierung', 'API Integration Experte', 'Automatisierung Agentur'],
en: ['Hire Automation Expert', 'n8n Developer for Hire', 'Workflow Automation Services', 'API Integration Expert', 'Process Automation Freelancer'],
sr: ['Automatizacija Procesa Srbija', 'n8n Ekspert', 'Automatizacija Radnih Tokova'],
},
useCases: {
de: [
'Lead-Erfassung und CRM-Updates',
@@ -261,6 +281,11 @@ export const services: ServiceData[] = [
],
},
technologies: ['Next.js', 'React', 'TypeScript', 'PostgreSQL', 'Prisma', 'Stripe', 'Vercel'],
seoKeywords: {
de: ['SaaS Entwickler beauftragen', 'MVP Entwicklung Freelancer', 'Next.js SaaS Entwicklung', 'React SaaS Plattform', 'SaaS Agentur Deutschland'],
en: ['Hire SaaS Developer', 'MVP Development Services', 'Next.js SaaS Development', 'React SaaS Platform', 'SaaS Development Agency'],
sr: ['SaaS Razvoj Srbija', 'MVP Razvoj', 'Next.js SaaS Razvoj'],
},
useCases: {
de: [
'Interne Tools & Dashboards',
@@ -324,6 +349,11 @@ export const services: ServiceData[] = [
],
},
technologies: ['React', 'Next.js', 'TypeScript', 'Python', 'FastAPI', 'Node.js', 'PostgreSQL', 'Docker'],
seoKeywords: {
de: ['Fullstack Entwickler beauftragen', 'Next.js Entwickler Freelancer', 'React Entwickler Deutschland', 'TypeScript Experte', 'Webentwicklung Agentur Köln'],
en: ['Hire Fullstack Developer', 'Next.js Developer for Hire', 'React Developer Freelance', 'TypeScript Expert', 'Fullstack Development Agency'],
sr: ['Fullstack Programer Srbija', 'Next.js Programer', 'React Programer Freelance', 'Web Razvoj Srbija'],
},
useCases: {
de: [
'Web-Anwendungen',
+281
View File
@@ -0,0 +1,281 @@
export interface TechnologyData {
slug: string;
name: string;
title: {
de: string;
en: string;
sr: string;
};
description: {
de: string;
en: string;
sr: string;
};
keywords: {
de: string[];
en: string[];
sr: string[];
};
features: {
de: string[];
en: string[];
sr: string[];
};
useCases: {
de: string[];
en: string[];
sr: string[];
};
relatedServices: string[]; // slugs from services.ts
}
export const technologies: TechnologyData[] = [
{
slug: 'nextjs',
name: 'Next.js',
title: {
de: 'Next.js Entwickler Köln | Next.js Agentur',
en: 'Next.js Developer | Next.js Agency',
sr: 'Next.js Programer | Next.js Agencija',
},
description: {
de: 'Professionelle Next.js Entwicklung für performante, SEO-optimierte Web-Anwendungen und SaaS-Produkte. Als erfahrener Next.js Entwickler aus Köln setze ich auf Server-Side Rendering, App Router und moderne React-Patterns für Ihr Projekt.',
en: 'Professional Next.js development for performant, SEO-optimized web applications and SaaS products. As an experienced Next.js developer, I leverage Server-Side Rendering, App Router and modern React patterns for your project.',
sr: 'Profesionalni Next.js razvoj za performantne, SEO-optimizovane web aplikacije i SaaS proizvode. Kao iskusni Next.js programer, koristim Server-Side Rendering, App Router i moderne React paterne za vaš projekat.',
},
keywords: {
de: ['Next.js Entwickler', 'Next.js Agentur', 'Next.js Freelancer', 'Next.js Köln', 'Next.js Deutschland', 'Next.js App Router', 'Next.js SSR', 'Server-Side Rendering Entwickler'],
en: ['Next.js Developer', 'Next.js Agency', 'Next.js Freelancer', 'Next.js Expert', 'Hire Next.js Developer', 'Next.js App Router', 'Next.js SSR', 'Server-Side Rendering Developer'],
sr: ['Next.js Programer', 'Next.js Agencija', 'Next.js Freelancer', 'Next.js Srbija', 'Next.js App Router', 'Next.js SSR'],
},
features: {
de: ['Server-Side Rendering (SSR) & Static Generation', 'App Router mit React Server Components', 'API Routes & Serverless Functions', 'Internationalisierung (i18n) mit next-intl', 'SEO-Optimierung & Core Web Vitals', 'Deployment auf Vercel & Self-Hosting'],
en: ['Server-Side Rendering (SSR) & Static Generation', 'App Router with React Server Components', 'API Routes & Serverless Functions', 'Internationalization (i18n) with next-intl', 'SEO optimization & Core Web Vitals', 'Deployment on Vercel & self-hosting'],
sr: ['Server-Side Rendering (SSR) & Static Generation', 'App Router sa React Server Components', 'API Routes & Serverless Functions', 'Internacionalizacija (i18n) sa next-intl', 'SEO optimizacija & Core Web Vitals', 'Deployment na Vercel & self-hosting'],
},
useCases: {
de: ['Corporate Websites & Landing Pages', 'SaaS-Plattformen & Dashboards', 'E-Commerce Shops', 'Multi-Language Portale', 'Blog & Content-Plattformen'],
en: ['Corporate Websites & Landing Pages', 'SaaS Platforms & Dashboards', 'E-Commerce Shops', 'Multi-Language Portals', 'Blog & Content Platforms'],
sr: ['Corporate Web Sajtovi & Landing Pages', 'SaaS Platforme & Dashboards', 'E-Commerce Prodavnice', 'Višejezični Portali', 'Blog & Content Platforme'],
},
relatedServices: ['saas-entwicklung', 'fullstack-entwicklung'],
},
{
slug: 'react',
name: 'React',
title: {
de: 'React Entwickler Köln | React Agentur',
en: 'React Developer | React Agency',
sr: 'React Programer | React Agencija',
},
description: {
de: 'Erfahrener React Entwickler für moderne, interaktive Web-Anwendungen. Ich entwickle skalierbare React-Frontends mit TypeScript, Tailwind CSS und modernem State Management für Unternehmen in Köln und deutschlandweit.',
en: 'Experienced React developer for modern, interactive web applications. I build scalable React frontends with TypeScript, Tailwind CSS and modern state management for businesses worldwide.',
sr: 'Iskusni React programer za moderne, interaktivne web aplikacije. Pravim skalabilne React frontend-ove sa TypeScript, Tailwind CSS i modernim state management-om za kompanije širom sveta.',
},
keywords: {
de: ['React Entwickler', 'React Agentur', 'React Freelancer', 'React Köln', 'React Deutschland', 'React TypeScript', 'React Hooks', 'React Komponenten'],
en: ['React Developer', 'React Agency', 'React Freelancer', 'React Expert', 'Hire React Developer', 'React TypeScript', 'React Hooks', 'React Components'],
sr: ['React Programer', 'React Agencija', 'React Freelancer', 'React Srbija', 'React TypeScript', 'React Hooks'],
},
features: {
de: ['React 19 mit Server Components', 'TypeScript-first Entwicklung', 'Custom Hooks & Composable Architecture', 'State Management (Zustand, Redux)', 'Component Libraries & Design Systems', 'Performance-Optimierung & Lazy Loading'],
en: ['React 19 with Server Components', 'TypeScript-first development', 'Custom Hooks & Composable Architecture', 'State Management (Zustand, Redux)', 'Component Libraries & Design Systems', 'Performance optimization & Lazy Loading'],
sr: ['React 19 sa Server Components', 'TypeScript-first razvoj', 'Custom Hooks & Composable Architecture', 'State Management (Zustand, Redux)', 'Component Libraries & Design Systems', 'Performance optimizacija & Lazy Loading'],
},
useCases: {
de: ['Single-Page Applications (SPA)', 'Admin Dashboards & Portale', 'Interaktive Datenvisualisierungen', 'Mobile-First Web-Apps', 'Design System Implementierung'],
en: ['Single-Page Applications (SPA)', 'Admin Dashboards & Portals', 'Interactive Data Visualizations', 'Mobile-First Web Apps', 'Design System Implementation'],
sr: ['Single-Page Applications (SPA)', 'Admin Dashboards & Portali', 'Interaktivne Vizualizacije Podataka', 'Mobile-First Web Aplikacije', 'Design System Implementacija'],
},
relatedServices: ['fullstack-entwicklung', 'saas-entwicklung'],
},
{
slug: 'typescript',
name: 'TypeScript',
title: {
de: 'TypeScript Entwickler Köln | TypeScript Experte',
en: 'TypeScript Developer | TypeScript Expert',
sr: 'TypeScript Programer | TypeScript Ekspert',
},
description: {
de: 'TypeScript-Experte für typsichere, wartbare und skalierbare Anwendungen. Ich entwickle mit TypeScript sowohl Frontend (React, Next.js) als auch Backend (Node.js, FastAPI) und sorge für höchste Code-Qualität.',
en: 'TypeScript expert for type-safe, maintainable and scalable applications. I develop with TypeScript both frontend (React, Next.js) and backend (Node.js, FastAPI), ensuring the highest code quality.',
sr: 'TypeScript ekspert za type-safe, održive i skalabilne aplikacije. Razvijam sa TypeScript-om i frontend (React, Next.js) i backend (Node.js, FastAPI), obezbeđujući najviši kvalitet koda.',
},
keywords: {
de: ['TypeScript Entwickler', 'TypeScript Experte', 'TypeScript Freelancer', 'TypeScript Köln', 'TypeScript Deutschland', 'Typsichere Entwicklung'],
en: ['TypeScript Developer', 'TypeScript Expert', 'TypeScript Freelancer', 'Hire TypeScript Developer', 'Type-safe Development', 'TypeScript Consultant'],
sr: ['TypeScript Programer', 'TypeScript Ekspert', 'TypeScript Freelancer', 'TypeScript Srbija', 'Type-safe Razvoj'],
},
features: {
de: ['Strenge Typisierung für fehlerfreien Code', 'Generics & Advanced Type Patterns', 'Zod Schema-Validierung', 'Fullstack TypeScript (Frontend + Backend)', 'Migration von JavaScript zu TypeScript', 'CI/CD mit TypeScript Checks'],
en: ['Strict typing for error-free code', 'Generics & Advanced Type Patterns', 'Zod Schema Validation', 'Fullstack TypeScript (Frontend + Backend)', 'JavaScript to TypeScript migration', 'CI/CD with TypeScript checks'],
sr: ['Stroga tipizacija za kod bez grešaka', 'Generics & Advanced Type Patterns', 'Zod Schema Validacija', 'Fullstack TypeScript (Frontend + Backend)', 'Migracija sa JavaScript na TypeScript', 'CI/CD sa TypeScript provere'],
},
useCases: {
de: ['Enterprise Web-Anwendungen', 'API-Entwicklung mit typsicheren Schemas', 'Monorepo-Projekte', 'Library & SDK Entwicklung', 'Legacy-Code Migration'],
en: ['Enterprise Web Applications', 'API development with type-safe schemas', 'Monorepo Projects', 'Library & SDK Development', 'Legacy Code Migration'],
sr: ['Enterprise Web Aplikacije', 'API razvoj sa type-safe šemama', 'Monorepo Projekti', 'Library & SDK Razvoj', 'Migracija Legacy Koda'],
},
relatedServices: ['fullstack-entwicklung', 'saas-entwicklung'],
},
{
slug: 'tailwind-css',
name: 'Tailwind CSS',
title: {
de: 'Tailwind CSS Entwickler Köln | Tailwind Experte',
en: 'Tailwind CSS Developer | Tailwind Expert',
sr: 'Tailwind CSS Programer | Tailwind Ekspert',
},
description: {
de: 'Tailwind CSS Experte für modernes, responsives und performantes UI-Design. Ich entwickle maßgeschneiderte Benutzeroberflächen mit Tailwind CSS v4, Custom Design Systems und Animationen.',
en: 'Tailwind CSS expert for modern, responsive and performant UI design. I build custom user interfaces with Tailwind CSS v4, custom design systems and animations.',
sr: 'Tailwind CSS ekspert za moderan, responsivan i performantan UI dizajn. Pravim prilagođene korisničke interfejse sa Tailwind CSS v4, custom design sistemima i animacijama.',
},
keywords: {
de: ['Tailwind CSS Entwickler', 'Tailwind CSS Experte', 'Tailwind CSS Freelancer', 'Tailwind CSS v4', 'CSS Framework Entwickler', 'UI Entwicklung'],
en: ['Tailwind CSS Developer', 'Tailwind CSS Expert', 'Tailwind CSS Freelancer', 'Tailwind CSS v4', 'CSS Framework Developer', 'UI Development'],
sr: ['Tailwind CSS Programer', 'Tailwind CSS Ekspert', 'Tailwind CSS Freelancer', 'Tailwind CSS v4', 'UI Razvoj'],
},
features: {
de: ['Tailwind CSS v4 mit CSS Layers', 'Custom Design Tokens & Themes', 'Responsive & Mobile-First Design', 'Dark Mode Implementierung', 'Framer Motion Animationen', 'Component Library Entwicklung'],
en: ['Tailwind CSS v4 with CSS Layers', 'Custom Design Tokens & Themes', 'Responsive & Mobile-First Design', 'Dark Mode Implementation', 'Framer Motion Animations', 'Component Library Development'],
sr: ['Tailwind CSS v4 sa CSS Layers', 'Custom Design Tokens & Themes', 'Responsive & Mobile-First Design', 'Dark Mode Implementacija', 'Framer Motion Animacije', 'Component Library Razvoj'],
},
useCases: {
de: ['Landing Pages & Marketing-Seiten', 'SaaS Dashboard Interfaces', 'E-Commerce Produktseiten', 'Mobile-First Responsive Design', 'Design System Aufbau'],
en: ['Landing Pages & Marketing Sites', 'SaaS Dashboard Interfaces', 'E-Commerce Product Pages', 'Mobile-First Responsive Design', 'Design System Development'],
sr: ['Landing Pages & Marketing Sajtovi', 'SaaS Dashboard Interfejsi', 'E-Commerce Stranice Proizvoda', 'Mobile-First Responsive Design', 'Design System Razvoj'],
},
relatedServices: ['fullstack-entwicklung', 'saas-entwicklung'],
},
{
slug: 'claude-ai',
name: 'Claude AI',
title: {
de: 'Claude AI Entwickler | Anthropic Claude Integration',
en: 'Claude AI Developer | Anthropic Claude Integration',
sr: 'Claude AI Developer | Anthropic Claude Integracija',
},
description: {
de: 'Spezialisiert auf Claude AI von Anthropic ich integriere Claude in Ihre Geschäftsprozesse für intelligente Automatisierung, Chatbots und KI-Agenten. DSGVO-konform und maßgeschneidert für Ihre Anforderungen.',
en: 'Specialized in Claude AI by Anthropic I integrate Claude into your business processes for intelligent automation, chatbots and AI agents. GDPR-compliant and tailored to your requirements.',
sr: 'Specijalizovan za Claude AI od Anthropic-a integrisem Claude u vaše poslovne procese za inteligentnu automatizaciju, chatbotove i AI agente. GDPR-usklađeno i prilagođeno vašim zahtevima.',
},
keywords: {
de: ['Claude AI Entwickler', 'Claude AI Integration', 'Anthropic Claude Experte', 'Claude API Entwickler', 'Claude AI Agentur', 'Claude AI DSGVO', 'Künstliche Intelligenz Claude'],
en: ['Claude AI Developer', 'Claude AI Integration', 'Anthropic Claude Expert', 'Claude API Developer', 'Claude AI Agency', 'Claude AI GDPR', 'Artificial Intelligence Claude'],
sr: ['Claude AI Developer', 'Claude AI Integracija', 'Anthropic Claude Ekspert', 'Claude API Programer', 'Veštačka Inteligencija Claude'],
},
features: {
de: ['Claude API Integration & Orchestrierung', 'Claude-basierte KI-Agenten', 'RAG-Systeme mit Claude', 'Claude für Dokumentenanalyse', 'Claude Computer Use & Tool Use', 'Multi-Model Orchestrierung (Claude + GPT)'],
en: ['Claude API Integration & Orchestration', 'Claude-based AI Agents', 'RAG Systems with Claude', 'Claude for Document Analysis', 'Claude Computer Use & Tool Use', 'Multi-Model Orchestration (Claude + GPT)'],
sr: ['Claude API Integracija & Orkestracija', 'Claude-bazirani AI Agenti', 'RAG Sistemi sa Claude', 'Claude za Analizu Dokumenata', 'Claude Computer Use & Tool Use', 'Multi-Model Orkestracija (Claude + GPT)'],
},
useCases: {
de: ['Intelligente Kundenservice-Chatbots', 'Automatische Dokumentenverarbeitung', 'Code-Review & Code-Generierung', 'Wissensmanagement mit RAG', 'Autonome KI-Agenten für Workflows'],
en: ['Intelligent Customer Service Chatbots', 'Automatic Document Processing', 'Code Review & Code Generation', 'Knowledge Management with RAG', 'Autonomous AI Agents for Workflows'],
sr: ['Inteligentni Chatbotovi za Korisničku Službu', 'Automatska Obrada Dokumenata', 'Code Review & Generisanje Koda', 'Upravljanje Znanjem sa RAG', 'Autonomni AI Agenti za Radne Tokove'],
},
relatedServices: ['ki-entwicklung', 'ki-agenten', 'gpt-integration'],
},
{
slug: 'python',
name: 'Python',
title: {
de: 'Python Entwickler Köln | Python Freelancer',
en: 'Python Developer | Python Freelancer',
sr: 'Python Programer | Python Freelancer',
},
description: {
de: 'Erfahrener Python Entwickler für Backend-Systeme, KI-Anwendungen und Automatisierung. Ich setze Python für FastAPI, Datenverarbeitung, Machine Learning und Prozessautomatisierung ein.',
en: 'Experienced Python developer for backend systems, AI applications and automation. I use Python for FastAPI, data processing, machine learning and process automation.',
sr: 'Iskusni Python programer za backend sisteme, AI aplikacije i automatizaciju. Koristim Python za FastAPI, obradu podataka, machine learning i automatizaciju procesa.',
},
keywords: {
de: ['Python Entwickler', 'Python Freelancer', 'Python Köln', 'Python Backend', 'FastAPI Entwickler', 'Python Automatisierung', 'Python KI'],
en: ['Python Developer', 'Python Freelancer', 'Python Backend', 'FastAPI Developer', 'Python Automation', 'Python AI', 'Hire Python Developer'],
sr: ['Python Programer', 'Python Freelancer', 'Python Backend', 'FastAPI Programer', 'Python Automatizacija', 'Python AI'],
},
features: {
de: ['FastAPI & Django Backend-Entwicklung', 'Python für KI & Machine Learning', 'Data Processing & Web Scraping', 'REST & GraphQL APIs', 'Automatisierung mit Python Scripts', 'Integration mit n8n & Zapier'],
en: ['FastAPI & Django Backend Development', 'Python for AI & Machine Learning', 'Data Processing & Web Scraping', 'REST & GraphQL APIs', 'Automation with Python Scripts', 'Integration with n8n & Zapier'],
sr: ['FastAPI & Django Backend Razvoj', 'Python za AI & Machine Learning', 'Obrada Podataka & Web Scraping', 'REST & GraphQL API-ji', 'Automatizacija sa Python Skriptama', 'Integracija sa n8n & Zapier'],
},
useCases: {
de: ['KI-Backend & API-Entwicklung', 'Datenverarbeitung & ETL Pipelines', 'Web Scraping & Data Mining', 'Chatbot & LLM Backends', 'Automatisierte Reporting-Systeme'],
en: ['AI Backend & API Development', 'Data Processing & ETL Pipelines', 'Web Scraping & Data Mining', 'Chatbot & LLM Backends', 'Automated Reporting Systems'],
sr: ['AI Backend & API Razvoj', 'Obrada Podataka & ETL Pipeline-ovi', 'Web Scraping & Data Mining', 'Chatbot & LLM Backend-ovi', 'Automatizovani Reporting Sistemi'],
},
relatedServices: ['ki-entwicklung', 'prozessautomatisierung', 'fullstack-entwicklung'],
},
{
slug: 'n8n',
name: 'n8n',
title: {
de: 'n8n Entwickler | n8n Automatisierung Experte',
en: 'n8n Developer | n8n Automation Expert',
sr: 'n8n Developer | n8n Automatizacija Ekspert',
},
description: {
de: 'Zertifizierter n8n Experte für professionelle Workflow-Automatisierung. Ich entwickle komplexe n8n Workflows, integriere KI (Claude, GPT) und migriere bestehende Automatisierungen von Zapier oder Make.',
en: 'Certified n8n expert for professional workflow automation. I develop complex n8n workflows, integrate AI (Claude, GPT) and migrate existing automations from Zapier or Make.',
sr: 'Sertifikovani n8n ekspert za profesionalnu automatizaciju radnih tokova. Razvijam kompleksne n8n radne tokove, integrisem AI (Claude, GPT) i migriam postojeće automatizacije sa Zapier ili Make.',
},
keywords: {
de: ['n8n Entwickler', 'n8n Experte', 'n8n Automatisierung', 'n8n Freelancer', 'n8n Workflow', 'n8n Self-Hosted', 'Zapier Alternative'],
en: ['n8n Developer', 'n8n Expert', 'n8n Automation', 'n8n Freelancer', 'n8n Workflow', 'n8n Self-Hosted', 'Zapier Alternative'],
sr: ['n8n Developer', 'n8n Ekspert', 'n8n Automatizacija', 'n8n Freelancer', 'n8n Radni Tok', 'n8n Self-Hosted'],
},
features: {
de: ['Custom n8n Workflow-Entwicklung', 'KI-Integration in n8n (Claude, GPT)', 'Migration von Zapier/Make zu n8n', 'Self-Hosted n8n Setup & Wartung', 'API-Integrationen & Webhooks', 'n8n Schulung & Support'],
en: ['Custom n8n Workflow Development', 'AI Integration in n8n (Claude, GPT)', 'Migration from Zapier/Make to n8n', 'Self-Hosted n8n Setup & Maintenance', 'API Integrations & Webhooks', 'n8n Training & Support'],
sr: ['Custom n8n Razvoj Radnih Tokova', 'AI Integracija u n8n (Claude, GPT)', 'Migracija sa Zapier/Make na n8n', 'Self-Hosted n8n Setup & Održavanje', 'API Integracije & Webhooks', 'n8n Obuka & Podrška'],
},
useCases: {
de: ['CRM-Automatisierung', 'E-Commerce Workflow-Automatisierung', 'Lead-Generierung & Scoring', 'Daten-Synchronisation', 'Social Media Automatisierung'],
en: ['CRM Automation', 'E-Commerce Workflow Automation', 'Lead Generation & Scoring', 'Data Synchronization', 'Social Media Automation'],
sr: ['CRM Automatizacija', 'E-Commerce Automatizacija Radnih Tokova', 'Generisanje Lead-ova & Scoring', 'Sinhronizacija Podataka', 'Automatizacija Društvenih Mreža'],
},
relatedServices: ['prozessautomatisierung', 'n8n-entwicklung'],
},
{
slug: 'ki-agenten',
name: 'KI-Agenten',
title: {
de: 'KI-Agenten Entwickler | AI Agent Development',
en: 'AI Agent Developer | AI Agent Development',
sr: 'AI Agent Developer | Razvoj AI Agenata',
},
description: {
de: 'Spezialisiert auf die Entwicklung autonomer KI-Agenten mit Claude AI, GPT-4 und Open-Source-Modellen. Ich baue intelligente Agenten, die eigenständig Entscheidungen treffen, Tools nutzen und komplexe Aufgaben erledigen.',
en: 'Specialized in developing autonomous AI agents with Claude AI, GPT-4 and open-source models. I build intelligent agents that independently make decisions, use tools and complete complex tasks.',
sr: 'Specijalizovan za razvoj autonomnih AI agenata sa Claude AI, GPT-4 i open-source modelima. Pravim inteligentne agente koji samostalno donose odluke, koriste alate i obavljaju kompleksne zadatke.',
},
keywords: {
de: ['KI-Agenten Entwicklung', 'AI Agent Developer', 'KI-Agenten Köln', 'Autonome KI', 'Multi-Agent System', 'Künstliche Intelligenz Agenten'],
en: ['AI Agent Development', 'AI Agent Developer', 'Autonomous AI', 'Multi-Agent System', 'Artificial Intelligence Agents', 'Hire AI Agent Developer'],
sr: ['Razvoj AI Agenata', 'AI Agent Developer', 'Autonomni AI', 'Multi-Agent Sistem', 'Veštačka Inteligencija Agenti'],
},
features: {
de: ['Autonome KI-Agenten mit Tool-Use', 'Multi-Agent-Systeme & Orchestrierung', 'Claude AI & GPT-4 basierte Agenten', 'RAG-integrierte Agenten', 'Menschliche Aufsicht & Kontrolle', 'Integration in bestehende Workflows'],
en: ['Autonomous AI Agents with Tool Use', 'Multi-Agent Systems & Orchestration', 'Claude AI & GPT-4 based Agents', 'RAG-integrated Agents', 'Human Oversight & Control', 'Integration into Existing Workflows'],
sr: ['Autonomni AI Agenti sa Tool Use', 'Multi-Agent Sistemi & Orkestracija', 'Claude AI & GPT-4 bazirani Agenti', 'RAG-integrisani Agenti', 'Ljudski Nadzor & Kontrola', 'Integracija u Postojeće Radne Tokove'],
},
useCases: {
de: ['Research-Agenten für Marktanalyse', 'Sales-Agenten für Lead-Qualifizierung', 'Support-Agenten für Ticketing', 'Content-Agenten für Marketing', 'DevOps-Agenten für Monitoring'],
en: ['Research Agents for Market Analysis', 'Sales Agents for Lead Qualification', 'Support Agents for Ticketing', 'Content Agents for Marketing', 'DevOps Agents for Monitoring'],
sr: ['Research Agenti za Analizu Tržišta', 'Sales Agenti za Kvalifikaciju Lead-ova', 'Support Agenti za Ticketing', 'Content Agenti za Marketing', 'DevOps Agenti za Monitoring'],
},
relatedServices: ['ki-agenten', 'ki-entwicklung', 'gpt-integration'],
},
];
export function getTechnologyBySlug(slug: string): TechnologyData | undefined {
return technologies.find((tech) => tech.slug === slug);
}
export function getAllTechnologySlugs(): string[] {
return technologies.map((tech) => tech.slug);
}
+25 -5
View File
@@ -59,26 +59,41 @@ export const seoKeywords: Record<Locale, string[]> = {
de: [
'Fullstack Developer',
'KI Entwickler',
'Künstliche Intelligenz',
'Claude AI Entwickler',
'Next.js Entwickler',
'React Entwickler',
'TypeScript Entwickler',
'Tailwind CSS',
'AI Agentenentwicklung',
'KI-Agenten Entwicklung',
'Prozessautomatisierung',
'Voice AI',
'n8n Automatisierung',
'Python Entwickler',
'Köln',
'Deutschland',
],
en: [
'Fullstack Developer',
'AI Developer',
'Claude AI Developer',
'Claude AI Integration',
'Next.js Developer',
'React Developer',
'TypeScript Developer',
'Tailwind CSS Developer',
'Artificial Intelligence',
'AI Agent Developer',
'AI Agent Development',
'Process Automation Expert',
'Voice AI Developer',
'n8n Automation',
'Python Developer',
'SaaS Development',
'Remote Developer Europe',
'Remote Developer USA',
'Remote Developer UK',
'Python Developer',
'React Developer',
'SaaS Development',
'Freelance AI Developer',
'London',
'New York',
@@ -90,10 +105,17 @@ export const seoKeywords: Record<Locale, string[]> = {
'Fullstack Programer',
'AI Programer Srbija',
'AI Developer Beograd',
'Claude AI Developer',
'Next.js Programer',
'React Programer',
'TypeScript Programer',
'Veštačka Inteligencija',
'Razvoj AI Agenata',
'Automatizacija Procesa',
'Voice AI Srbija',
'n8n Automatizacija',
'Python Programer Srbija',
'React Programer Beograd',
'Beograd',
'Novi Sad',
'Niš',
@@ -101,8 +123,6 @@ export const seoKeywords: Record<Locale, string[]> = {
'Subotica',
'Srbija',
'Softverski Razvoj Srbija',
'Python Programer Srbija',
'React Programer Beograd',
'Remote Developer Dijaspora',
'Freelance Programer Srbija',
],
+8 -7
View File
@@ -1,7 +1,8 @@
@import './colors.css';
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "tailwindcss";
@plugin "tailwindcss-animate";
@plugin "@tailwindcss/forms" {
strategy: class;
}
/* Font loading states */
html.fonts-loading body {
@@ -27,7 +28,7 @@ html.fonts-fallback body {
@apply text-foreground antialiased;
background-color: transparent !important;
}
#root {
background-color: transparent !important;
}
@@ -59,7 +60,7 @@ html.fonts-fallback body {
.text-gradient {
@apply bg-clip-text text-transparent bg-gradient-to-r from-primary via-accent to-secondary;
}
/* GPU acceleration utilities - simplified */
.gpu-accelerated {
transform: translateZ(0);
@@ -161,4 +162,4 @@ a, button {
a {
@apply min-h-[44px] min-w-[44px];
}
}
}
-24
View File
@@ -1,11 +1,6 @@
import { chain, chainMatch, isPageRequest, csp } from '@next-safe/middleware';
import { NextRequest, NextResponse } from 'next/server';
import createMiddleware from 'next-intl/middleware';
import { locales, defaultLocale } from './i18n/config';
import { randomBytes } from 'crypto';
const CSRF_TOKEN_COOKIE_NAME = 'csrf_token';
const CSRF_TOKEN_LENGTH = 32;
const handleI18nRouting = createMiddleware({
locales,
@@ -28,25 +23,6 @@ const securityMiddleware = csp({
},
});
// Custom middleware to handle CSRF tokens
function csrfMiddleware(request: NextRequest, response: NextResponse): NextResponse {
const existingToken = request.cookies.get(CSRF_TOKEN_COOKIE_NAME);
if (!existingToken) {
const newToken = randomBytes(CSRF_TOKEN_LENGTH).toString('base64url');
response.cookies.set(CSRF_TOKEN_COOKIE_NAME, newToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict',
maxAge: 60 * 60 * 24, // 24 hours
path: '/',
});
}
return response;
}
// Use chain to combine i18n middleware with security middleware
// First run i18n, then apply CSP only on page requests
export default chain(
-81
View File
@@ -1,81 +0,0 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ["class"],
content: [
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
},
animation: {
'slide-up': 'slideUp 0.5s ease-out forwards',
'fade-in': 'fadeIn 0.3s ease-out forwards',
},
keyframes: {
slideUp: {
'0%': { transform: 'translateY(10px)' },
'100%': { transform: 'translateY(0)' },
},
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
},
colors: {
border: "rgb(var(--border))",
input: "rgb(var(--input))",
ring: "rgb(var(--ring))",
background: "rgb(var(--background))",
foreground: "rgb(var(--foreground))",
primary: {
DEFAULT: "rgb(var(--color-primary))",
dark: "rgb(var(--color-primary-dark))",
},
secondary: {
DEFAULT: "rgb(var(--color-secondary))",
gray: "rgb(var(--color-secondary-gray))",
fuzzy: "rgb(var(--color-secondary-fuzzy))",
},
text: {
DEFAULT: "rgb(var(--color-text))",
light: "rgb(var(--color-text-light))",
},
destructive: {
DEFAULT: "rgb(var(--destructive))",
foreground: "rgb(var(--destructive-foreground))",
},
muted: {
DEFAULT: "rgb(var(--muted))",
foreground: "rgb(var(--muted-foreground))",
},
accent: {
DEFAULT: "rgb(var(--accent))",
foreground: "rgb(var(--accent-foreground))",
},
popover: {
DEFAULT: "rgb(var(--popover))",
foreground: "rgb(var(--popover-foreground))",
},
card: {
DEFAULT: "rgb(var(--card))",
foreground: "rgb(var(--card-foreground))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
},
},
plugins: [
require("tailwindcss-animate"),
require("@tailwindcss/forms")({
strategy: "class",
}),
],
}
+1 -1
View File
@@ -15,7 +15,7 @@
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"jsx": "react-jsx",
"incremental": true,
"plugins": [
{