The promotional website for TabMates, published at tabmates.de.
This repository contains only the marketing site. It is not the app.
| What | Where |
|---|---|
| This site (marketing) | tabmates.de — this repo |
| The actual app (Web/PWA) | app.tabmates.de — TabMates/app |
| Backend | TabMates/server |
Static Astro site, built to plain HTML/CSS and served by GitHub Pages. No analytics, no cookies, and no third-party requests at runtime — that is a deliberate constraint, not an accident, because the site makes that claim on the privacy section of the landing page.
Requires Node 22 (see .nvmrc).
npm ci
npm run dev # http://localhost:4321| Script | Does |
|---|---|
npm run dev |
Dev server with hot reload |
npm run build |
Static build into dist/ |
npm run preview |
Serve the built dist/ locally |
npm run check |
astro check — TypeScript and template diagnostics |
npm run format |
Prettier, write |
npm run format:check |
Prettier, verify only (this is what CI runs) |
npm run og |
Regenerate public/og-image.png from the feature graphic |
src/
├─ assets/
│ ├─ brand/feature-graphic.png source for the OG image
│ ├─ logo.svg the mark, copied from the app repo
│ └─ screenshots/*-{light,dark}.png
├─ components/
│ ├─ Landing.astro composes the whole landing page
│ ├─ Hero / FeatureGrid / ... one file per section
│ └─ ui/ Button, Card-ish primitives, Icon, PhoneFrame
├─ i18n/{en,de,utils}.ts all user-facing copy
├─ integrations/placeholder-check.ts fails the deploy on unfilled TODOs
├─ layouts/{Base,Legal}.astro
├─ pages/
│ ├─ index.astro privacy.mdx imprint.mdx account-deletion.mdx 404.astro
│ └─ de/index.astro de/privacy.mdx de/imprint.mdx de/account-deletion.mdx
├─ styles/global.css design tokens
└─ config.ts every outbound URL + launch flags
src/styles/global.css is a port, not a design. Every colour, radius and type
size is copied from the app's own design system so the site and the app read as one
product. If the app's palette changes, change it here to match.
| Token group | Source of truth in TabMates/app |
|---|---|
| Light colours | core/designsystem/.../theme/Color.kt:5-33 |
| Dark colours | core/designsystem/.../theme/Color.kt:35-63 |
| positive/negative | core/designsystem/.../theme/Color.kt:65-87 (the ExtendedColors block) |
| Corner radii | core/designsystem/.../theme/Shape.kt:7-14 |
| Type scale | core/designsystem/.../theme/Type.kt:10-131 |
| Logo | docs/images/logo.svg (cream #FBE9C7 + burnt orange #D2762A) |
Two intentional deviations from a pure port, both documented inline in
global.css:
- Font. The app uses
FontFamily.Default(Roboto on Android). The site self-hosts Inter Variable, because a system-font stack renders the hero headline differently on every OS. Inter is bundled from npm and served from this domain — never Google Fonts, which would be both a third-party request and a GDPR problem on a German site. - Tracking on display sizes. Inter needs slightly tighter letter-spacing than
Roboto above 45px. Everything from
headlinedown is a 1:1 conversion.
All user-facing text lives in src/i18n/en.ts and src/i18n/de.ts. No copy is
hardcoded in a component.
- Add or change the key in
en.tsfirst — it is the source of truth. - Add the same key to
de.ts.
de.ts is typed as Strings (i.e. typeof en), so forgetting step 2 is a build
error, not a paragraph that silently ships in English. Run npm run check to see
it.
German pages currently reuse the English slugs (/privacy ↔ /de/privacy). To
switch to translated slugs like /de/datenschutz, fill in LOCALIZED_SLUGS in
src/i18n/utils.ts and rename the page files — the header language switch and the
hreflang tags both already route through that map.
Push to main → .github/workflows/deploy.yml builds and publishes to GitHub
Pages. Pull requests run .github/workflows/ci.yml (format, type-check, build,
offline link check).
The deploy build runs with CHECK_PLACEHOLDERS=strict, which fails the build
while any TODO: placeholder remains in the output, so a half-finished page can
never reach production. Local and PR builds only print a warning. Nothing ships a
placeholder today — the guard is there for the next draft that does.
GPL-3.0-or-later, matching the app. See LICENSE.