mAi a06a94ff58 feat: #13 Light/Dark + EN/DE Toggle — Shift-2 Rollout
Rollout des Toggle-Patterns auf alle 57 statischen Sites (dasbes.de + dumusst.com sind dynamic, kein index.html).

1. **Bulk-Wiring (53 Sites)** via tools/patch-theme.py:
   - Anti-FOUC inline IIFE im <head> (vor erstem Paint)
   - <link rel="stylesheet" href="/shared/css/theme.css">
   - <script src="/shared/theme.js"> + toggles.js (i18n.js bleibt, hängt sich ans neue Widget)

2. **Per-Site Light-Overrides (14 Sites)** via tools/patch-light-overrides.py:
   - 6034, allainallain, commanderkin, hallofraumaier, heygoldi, keinefreun, lexsiebels, machesdocheinfach, matthiasbreier, orakil, osterai, patentonkel, traihard, wartebitte
   - Pro Site nur die failing accent-vars darkened (--green-dim, --text-faint, --warm-dim, --gold-dim, etc.)
   - AA 4.5:1+ auf white bg gesichert; Brand-Akzent erhalten

3. **data-theme-lock="dark" (4 Sites)** auf <html>:
   - kilibri, killusion, killionaer, killuminati
   - Aesthetisch dark-only — toggles.js blendet Theme-Button automatisch aus, Lang-Button bleibt

4. **Footer-Toggle Removal (52 Sites)** via tools/remove-footer-toggle.py:
   - Bestehende footer [data-i18n-toggle] Buttons entfernt — top-right widget übernimmt
   - Disclaimer-Information in tooltip des neuen Buttons + ai-disclosure.js footer

QA:
- ./build.sh: 59/59 sites built clean
- contrast-audit.py --both: 0/59 dark fail, 0/59 light fail
- anti-ai-lint: 0/57 sites flagged

Tools committed (idempotent, für Wiederverwendung):
- tools/patch-theme.py (--all wired alle Sites)
- tools/patch-light-overrides.py (per-site OVERRIDES dict)
- tools/remove-footer-toggle.py (4 Patterns für versch. Footer-Strukturen)
2026-05-08 11:16:15 +02:00
2026-04-30 02:50:50 +02:00

onepager

Mono-repo for 40+ vanity domain onepager sites. Single nginx container with template system and server_name-based routing.

Structure

sites/           # One folder per domain
  example.de/
    site.yaml    # Domain config, template choice, variables
    index.html   # Content (generated or hand-crafted)
    assets/      # Optional images, fonts
templates/       # Shared HTML templates
shared/css/      # Shared CSS (variables, responsive, animations)
nginx/           # Generated nginx.conf + generator script
build/           # Generated output (gitignored)

Usage

Add a new site

# Templated site
./add-site.sh example.de --template person-dark --name "Max Mustermann"

# Custom HTML site
./add-site.sh example.de --template custom

Build

./build.sh              # build + anti-AI text lint
./build.sh --skip-lint  # build only (emergencies)

Requires yq for YAML parsing and python3 for the lint step. Outputs to build/.

Anti-AI text lint

Every build runs tools/anti-ai-lint.py against build/<domain>/index.html, flagging text fingerprints typical of LLM-generated content (vocab and structure patterns from tools/anti-ai-blacklist.yaml). Severity warn prints a message; fail aborts the build.

Whitelist a hit:

  • HTML comment in the affected page: <!-- anti-ai-allow: revolutionär, em-dash-3-bullet -->
  • Per-site override in site.yaml:
    anti_ai_allow:
      - revolutionär
      - em-dash-3-bullet
    

The blacklist source is docs/geo-seo-guideline.md §3.6. Test the linter with tools/test-anti-ai-lint.sh.

Deploy

Push to main — Dokploy auto-deploys. All domains must be configured in Dokploy.

Templates

Template Description
person-dark Professional profile, dark theme
person-light Professional profile, light/cream theme
product-dark Product/service landing page, dark
editorial Long-form manifesto/editorial style
fun Playful/personal pages
minimal Bare-bones single section
custom Hand-crafted HTML, no rendering

site.yaml

domain: example.de
aliases: [www.example.de]
template: person-dark
title: "Page Title"
description: "Meta description"
lang: de

vars:
  name: "Name"
  role: "Role"
  initials: "AB"
  tagline: "Tagline here"
  accent: "#c9a84c"
  accent_light: "rgba(201, 168, 76, 0.1)"
  font_primary: "Inter"
  font_secondary: "Newsreader"
  tags: ["Tag 1", "Tag 2"]
  sections:
    - type: features
      title: "Section Title"
      items:
        - title: "Item"
          desc: "Description"
    - type: profile
      bio: "Bio text"
  cta:
    text: "Contact"
    href: "mailto:info@example.de"

schema:
  type: Person
  name: "Erika Mustermann"
  url: "https://example.de/"
  jobTitle: "Patentanwältin"
  sameAs:
    - https://www.linkedin.com/in/erika-mustermann/
    - https://github.com/erika-mustermann

Schema.org / JSON-LD (GEO/SEO)

Templated sites can declare a schema: block in site.yaml. render.sh emits it as <script type="application/ld+json">…</script> inside <head> (slot {{schema_jsonld}} in templates/base.html). See docs/geo-seo-guideline.md §3.3 for rationale.

Conventions

  • schema.type@type (the YAML key is type, the rendered key is @type).
  • @context: https://schema.org is added automatically.
  • Nested objects use the JSON-LD form directly: write "@type": Organization (quoted because of the @).
  • Array fields like sameAs: are passed through as JSON arrays.
  • If schema: is absent, no <script> tag is emitted (empty slot).
  • If schema.type is omitted, the template default applies:
    • person-dark, person-lightPerson
    • product-darkProduct
    • editorialArticle
    • fun, minimal → no default (set type: explicitly).

Supported types include Person, Organization, Article, Product, FAQPage, LocalBusiness. Schema.org accepts any type — these are just the ones we use most.

Custom sites

template: custom skips rendering, so the slot is not applied. Hand-craft the JSON-LD directly inside index.html — typically right before </head>.

Testing

./tests/schema-test.sh

Renders fixture files in tests/fixtures/ and validates that JSON-LD is well-formed, has the correct @context/@type, and that sites without schema: produce no script tag.

For Schema.org-validator checks (recommended for any new live site that uses the slot), paste the rendered <script type="application/ld+json"> block into https://validator.schema.org/.

  • Issue #341: Onepager Mono-Repo
  • Issue #335: Container consolidation
Description
Mono-repo for 40+ vanity domain onepager sites — single nginx container with template system
Readme 2.2 MiB
Languages
HTML 93.1%
Python 2.6%
Shell 2.2%
JavaScript 1.8%
CSS 0.3%