Arteon is a Kraków art society run by working, recognised musicians — concerts, stage works, musical initiatives, music therapy. The organisation is led by Mateusz Kluza (President, double bassist, Fryderyk Award nomination 2024, concerts with Andrea Bocelli and Krzysztof Penderecki).
The society existed online only as scattered social-media profiles — no domain of its own, no event portfolio, no registration details an official could read. I built a new site from scratch, extended it with a panel for self-managing events, full SEO, bilingualism, and deployment to a server.
📋 Project metrics
- Status: Completed, the site is in production (arteon.org.pl)
- Role: Full-Stack Developer / Architect / UX Designer — the whole project from strategy to deployment
- Scope: Strategy and content planning, design system, admin panel (CMS), frontend implementation, SEO, accessibility, production deployment
- Goal: Turn an absence online into a site that, in a few seconds, convinces an institutional partner that Arteon is a serious, credible organisation
🚀 How the project came together
We started (together with the client) by understanding what Arteon actually is — not an event agency that only coordinates, and not a bureaucratic NGO, but a society built by working musicians out of genuine passion. That distinction had to be visible in every layer of the site: in the content, in the event structure, in the design itself.
From there the work went in several directions in parallel: bilingualism of the site (Polish and English, the same URL — content switches via a cookie), a custom event management system built on Strapi so the client could independently add concerts, galleries and programmes without writing code. New content from the panel appears on the site within a few minutes — no manual redeploy.
The next big stage was the design system: cinematic noir, gold on deep black, lapidary Cinzel (Roman inscription, Trajan) consistent with the client's A monogram. Deliberate negative space, no gradient blobs, glassmorphism or cheap effects — photographs, typography and gold carry the look.
Then full SEO: metadata, structured data recognised by Google (Organization with KRS/NIP/REGON, Event, BreadcrumbList, Person), a sitemap, dynamically generated social preview images (Open Graph + Twitter), registration details readable when someone verifies the society. Finally a round of accessibility fixes — so the site could be operated by keyboard, by screen reader, and so gold-on-black contrast met WCAG 2.2 AA. Deployment to a VPS via Docker Compose with Traefik (TLS, Let's Encrypt), health checks and noindex for the CMS panel.
🎯 Business problem
A young art society existed online only as scattered social-media profiles — no domain of its own, no event portfolio, no readable registration details. Someone searching Google for Arteon landed on social accounts, not on a place that shows who the organisation is and what it has already done.
❌ Specific problems
- No single, credible place online. An institution, venue or partner checking Arteon in a few seconds needs to see the statute, KRS/NIP/REGON, body of work and production scale. That wasn't there — only scattered social posts.
- Two audiences, one message. The site has to convince decision-makers (institutions, venues, partners), not entertain a mass audience. The public are beneficiaries of the events, not the target of persuasion. Primary read: phone, on the move.
- No event portfolio. Every delivered concert is potential proof of scale, but adding a new event required a developer, so nobody did it.
- Bilingualism without duplication. PL/EN required because of foreign partners.
- SEO for a niche. Competing on a name and local phrases needs structured data and a dynamic sitemap — and decision-makers Google Arteon first.
- Accessibility as mission. Arteon states that art is for people with disabilities — the site has to meet WCAG 2.2 AA.
- A narrow delivery window. ~10 days for an MVP, ready to grow without a rebuild.
💡 How it was solved
- Persuasion, not a business card. Each section walks a decision-maker through proofs of credibility in the order they look for them: a hero with scale → about (mission, board with links to members' portfolios) → events (upcoming + archive) → support us (a tax-deductible donation) → contact with registration details. Mobile-first, because the decision-maker checks from a phone, on the move.
- Cinematic noir as a status tool. Deep shadows, gold on black (6.43:1 contrast — WCAG AA), Cinzel capitals, deliberate negative space. Photographs, typography and gold carry the look, not cheap effects. Cinematic noir on purpose: card thumbnails and the hero in greyscale, the event-detail gallery in colour.
- Bilingualism without a second copy of the site. Language in a cookie, Polish by default. The same
/wydarzenia/[slug]address for both languages — the switcher refreshes the content, it does not move you to another path. Fewer dependencies, one URL to send a partner. - A panel the site does not crash on. CMS data is mapped to a flat shape — an empty field yields a fallback, not a white screen. The build still passes if the panel is briefly unavailable; content appears on the first request after start.
- The English draft creates itself. A new Polish event automatically gets an English sketch (text fields + programme) for manual translation. Editing the Polish version does not overwrite English that has already been translated.
- The event address works in both languages. The URL identifier is shared and the panel does not copy it into the English version. The front stitches the Polish record (address, date, photos) to the English one (title, venue, description, programme) — if a translation is missing, it shows Polish.
- Event split is automatic. Concerts are fetched once and split into upcoming and archive by date — no manual flag.
- SEO for decision-makers and crawlers. Metadata on every page, structured data (organisation with KRS/NIP/REGON, event, breadcrumbs, person), a sitemap, a file for bots, generated social preview images with a cover fallback.
- Accessibility from the start. Landmark + SkipLink, gold focus, ARIA labels, semantic HTML, lightbox with a focus trap and keyboard, gold-on-black contrast at AA.
- Statute as text, not a PDF. A separate
/statutpage with the full text (not a download) — readable for an official and for bots. On the homepage only a teaser in an accordion and a link.
📈 Result
| Area | Before | After deployment |
|---|---|---|
| Web presence | Social media only, no domain | Own domain + bilingual site |
| Credibility for a partner | Scattered social media, no registration details | Statute, KRS/NIP/REGON, body of work, scale — readable in a few seconds |
| Event management | Manual, required a developer | CMS panel — the client adds events themselves, no code |
| Time to publish a new event | Site redeploy | Refresh within a few minutes, no redeploy |
| Event split | None | Automatic upcoming/archive by date |
| Google visibility | No structured data | JSON-LD + dynamic sitemap + OG/Twitter images |
| Accessibility | Not checked | WCAG 2.2 AA — focus trap, ARIA, skip link, contrast verified |
| Languages | Polish only | Polish and English, cookie-based switcher |
"The society had working musicians and real productions behind it, but online you couldn't see any of it. I built a site that shows the statute, registration details and body of work in a few seconds — and the client adds events themselves, without a developer."
🛠️ Technical challenges
- A flaw in the CMS translations. The panel does not copy the event URL into the English version — the field stays empty. The front always reads the Polish record (address, date, photos) and attaches the English texts. Missing translation = Polish fallback, the site does not crash.
- EN skeleton only on first create. When a Polish event is added, a panel hook copies texts and the programme into an English sketch. On publish the hook fires again — it first checks whether the English version already exists, so it is not duplicated. Later Polish edits do not overwrite the translation.
- Build independent of the panel. If the CMS does not respond while the image is building, the event list is empty and the image is still produced. After start, the first visit to the site pulls the content.
- Date filter outside the panel. Date filtering in the CMS is unreliable. I fetch all events and split them on the front by today's date.
- Panel images via a public address. The Next.js optimiser fetches the file itself — the container's internal address is invisible to it. Media URLs are rewritten to the panel's public URL.
- Social images without a CSS font. The Open Graph generator does not read the site's fonts — it needs TTF files. Local Cinzel and Inter, composition like the hero. A separate image for X (unreliable fallback from og:image). Event: its own social crop or the cover.
- Lightbox outside section layers. The modal goes to
document.bodyso a parentoverflowdoes not clip it. Focus trap, Esc and arrows. - Panel HTML does not go in raw. CMS richtext is sanitised before it hits the page.
- Own network on a shared VPS. Prefix on container and volume names, a separate DNS alias to the panel (so it does not collide with Traefik from other projects), panel with
noindex.
🛠️ Architecture and tech stack
- Frontend: Next.js 16 (App Router, Turbopack), React 19, TypeScript (strict, noUncheckedIndexedAccess), Tailwind CSS 4
- Typography: next/font/google — Cinzel (serif, the whole display layer) + Inter (sans, body)
- Content panel (CMS): Strapi v5 + PostgreSQL 16 (headless, server-side only)
- Event content type: bilingual fields in the Strapi panel (PL/EN, built-in CMS i18n — not a frontend library), drafts and publish, programme as a repeatable component, horizontal/vertical cover, gallery, separate social image
- Data access: Strapi REST, one event shape on the front, PL/EN stitch, request cache, content refresh every 5 min
- Frontend i18n: custom, no library (cookie, Polish default, same URL),
messages/pl.jsonandmessages/en.jsonpacks - Sanitisation: isomorphic-dompurify on panel richtext
- Images: next/image (AVIF/WebP, lazy load, remotePatterns for the panel), dynamic OG/Twitter (Satori + local TTF)
- Structured data: JSON-LD (Organization with PropertyValue KRS/NIP/REGON, Event, BreadcrumbList, Person)
- Accessibility: WCAG 2.2 AA — SkipLink, gold focus-visible, focus trap (lightbox), ARIA, semantic HTML, contrast verified
- SEO: metadata per page, alternates.canonical, dynamic sitemap + robots, OG/Twitter images
- Deployment: Docker Compose (dev + prod), Traefik (TLS Let's Encrypt), multi-stage Dockerfile (node:22-alpine), health checks, CMS noindex, private network alias
🎨 Key design decisions
- Cinematic noir + Roman inscription. Deep black, gold, Cinzel (Trajan's column) consistent with the client's A monogram. Restrained prestige — art treated with reverence, no marketing noise and no gen-AI aesthetics (gradient blobs, glassmorphism, parallax).
- The A monogram as deliberate sacrum. A jewel in the crossbar of the A — brand intent (art + teon = art + God), not hiding the symbol.
- PNG logo approved by the client. Monogram in the header + wordmark with dots over A and E in the hero (a signature in one place, not diluted in the footer). SVG stays as a fallback — sharp, themeable, 4.5 KB, reversible.
- One-page persuasion. Simple structure: About / Events / Contact plus a separate event view and statute. No deep IA — the decision-maker scrolls proofs of credibility in one pass.
- Statute as text, not a PDF. A separate
/statutpage with the full text — readable for an official and for crawlers. On the homepage only a teaser in an accordion. - Support us with donation instructions. Account number, transfer title, link to the official Ministry of Finance tax relief — a clear path for a donor, no payment gateway.
- Tokens as the single source of truth. Colours, typography, spacing, radius in
@themein globals.css. Components stay on tokens; hex is hardcoded only in constants for the OG image generator (kept in sync with the tokens so they do not drift). - Server Components by default. use client only where necessary (state, events, browser hooks — header, language switcher, event list, lightbox, i18n provider). Layers: app/ (routing, metadata, JSON-LD), lib/ (pure functions, data access), components/ (atoms, sections, chrome, i18n), content/ (organisation and statute entities), messages/ (UI strings).
Artifacts
- Site: https://arteon.org.pl
