Rene De Ren 241411054e feat: rebrand to R&D-lab + WBD palette + lab-slide cards
Identity refresh aligned to Waterschap Brabantse Delta.

Brand
- HELIX → R&D-lab everywhere user-facing (SITE.name; literal "HELIX"s
  swept across routes, app.html, login, error messages, seed).
- New tagline: "Projects, innovations, and every strand between."
- Site description updated.

Palette (sourced from the official WSBD-logo.svg)
- Primary #0d4f9e, secondary #1fa0db, accent #bed137 — added as
  --wbd-blue / --wbd-cyan / --wbd-lime CSS vars and as wbd.* in
  tailwind.config.js. helix.* aliases now point to the WBD palette.
- Strand A (Projects) → #1fa0db cyan. Strand B (Innovations) → #bed137 lime.
- Body vignette + scroll-bar + legend dots repainted accordingly.

Composite logo
- New 24px nav glyph + favicon.svg: WBD-style tilted-square mark in WBD
  blues at the centre, helix strands (lime + cyan) wrapping it, lime
  "active site" dot at the crossing. Says "R&D-lab × Brabantse Delta"
  in one mark.

Lab-slide cards (VerticalHelix)
- Frosted-glass surface (backdrop-filter blur+saturate).
- Thick 5px strand-coloured stripe on the helix-facing edge (gradient,
  glowing shadow). Slide rounds the stripe corners; the rest is square.
- Slide header has the strand badge and a monospace serial number
  (01/03 etc) — lab-specimen feel.
- Dashed footer rule + "Open detail →" CTA.
- Inline link chips (Gitea / Dashboard / Demo / Docs / Paper / Video)
  with inline SVG icons + short labels. Hover lights up in the strand
  colour. Capped at 5 visible, "+N" overflow indicator.
- Real <a> chips inside the card without nested <a>: overlay-link
  pattern (transparent slide-link absolute fills the card, chips sit on
  z-index: 2 above it).

Server load
- + Page now fetches each project's links in one Drizzle relational
  query (db.query.projects.findMany with: { links }), capped at 12.
- + Form: strand picker (Project / Innovation radios) reads + persists
  the new column.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 12:13:57 +02:00

HELIX

EVOLV and every R&D strand, one helix.

The R&D showcase platform of Waterschap Brabantse Delta. EVOLV at its core, every innovation along the strands.

HELIX collects projects, innovations, and updates from across the R&D team in one place — with deep links to repos, dashboards, and demos. Sign-in is gated to the RnD Gitea organisation; viewing is open.

Stack

  • SvelteKit 2 + Svelte 5 + TypeScript
  • Tailwind v4 (CSS-first design tokens)
  • SQLite (single file) + Drizzle ORM
  • Gitea OAuth2 for authoring (no passwords stored)
  • Pure SVG + CSS helix animation — no WebGL

Run it locally — two ways

Mirrors how the EVOLV stack is exercised locally. One command, named volume for SQLite, healthcheck wired:

cd /path/to/helix
docker compose up -d --build         # build image + start container
docker compose logs -f helix         # tail logs
# → http://localhost:3000

If port 3000 is in use (Node-RED, another dev server, …) pick another:

HELIX_PORT=3030 \
  ORIGIN=http://localhost:3030 \
  GITEA_REDIRECT_URI=http://localhost:3030/auth/gitea/callback \
  docker compose up -d --build
# → http://localhost:3030

Useful commands:

docker compose ps                                 # status + health
docker inspect helix --format '{{.State.Health.Status}}'
docker compose exec helix sh                      # shell into the container
docker compose exec helix sqlite3 /data/helix.db  # inspect the DB
docker compose down                               # stop (keeps the volume)
docker compose down -v                            # stop + DELETE all data

The SQLite database lives in the helix_helix-data named volume at /data/helix.db. Migrations + the idempotent seed both run on every boot. Once you have real content, set SEED_ON_BOOT=false in your env to stop re-seeding the demo entries.

2. Native Node (faster dev loop, hot reload)

nvm use                # .nvmrc → Node 20 (Tailwind v3 also works on 18)
cp .env.example .env   # fill in Gitea OAuth — see below

npm install
npm run db:generate    # generate drizzle/0000_*.sql from the schema
npm run db:migrate     # apply migrations to ./helix.db
npm run db:seed        # idempotent demo content
npm run dev            # http://localhost:5173

Gitea OAuth setup

HELIX uses your existing Gitea identity. Create an OAuth2 application once:

  1. Open https://gitea.wbd-rd.nl/-/user/settings/applications
  2. Click Create new OAuth2 application
  3. Application name: HELIX (local) (or HELIX (production))
  4. Redirect URI:
    • local: http://localhost:3000/auth/gitea/callback
    • prod: https://<your-helix-host>/auth/gitea/callback
  5. Click Create application
  6. Copy the Client ID and Client Secret into your .env:
    GITEA_CLIENT_ID=...
    GITEA_CLIENT_SECRET=...
    

GITEA_ALLOWED_ORG=RnD restricts authoring (sign-in) to members of the RnD organisation. Leave it empty to allow any authenticated Gitea user.

Adding a dashboard embed allowlist host

src/lib/config.tsDASHBOARD_ALLOWED_HOSTS controls which Grafana / dashboard origins can be embedded inline on a project page. Add a hostname (no scheme, no path) and redeploy. Hosts not on the list render as a "Open in new tab" card instead — never blindly iframed.

Production deploy

Same compose file as local. On the production host:

cp .env.example .env
# Set: ORIGIN=https://your-host, GITEA_REDIRECT_URI=https://your-host/auth/gitea/callback,
#      GITEA_CLIENT_ID, GITEA_CLIENT_SECRET (from a separate prod OAuth app)
docker compose up -d --build

Put a TLS-terminating reverse proxy (nginx/caddy/traefik) in front of port 3000. Back up the volume periodically:

docker compose exec helix sqlite3 /data/helix.db .dump | gzip > helix-backup-$(date +%F).sql.gz

Project layout

helix/
├── src/
│   ├── lib/
│   │   ├── components/         # Helix.svelte, ProjectCard, PostCard, Nav, Footer, DashboardEmbed
│   │   ├── server/
│   │   │   ├── db/             # Drizzle schema + client (better-sqlite3)
│   │   │   ├── auth.ts         # session token + cookie helpers
│   │   │   └── gitea.ts        # OAuth2 dance + user fetch
│   │   ├── config.ts           # site name, tagline, dashboard allowlist
│   │   └── markdown.ts         # marked wrapper + slug/id helpers
│   ├── routes/
│   │   ├── +page.svelte        # landing (helix hero + recent projects/posts)
│   │   ├── projects/           # /projects, /[slug], /new
│   │   ├── posts/              # /posts, /[slug], /new
│   │   ├── login/              # sign-in page (kicks off Gitea OAuth)
│   │   ├── logout/+server.ts   # POST → invalidates session
│   │   └── auth/gitea/         # OAuth start + callback
│   ├── app.html
│   ├── app.css                 # Tailwind v4 entry + design tokens
│   ├── app.d.ts                # Locals types
│   └── hooks.server.ts         # session hydration
├── drizzle/                    # generated migrations
├── scripts/
│   ├── migrate.js              # run pending migrations
│   └── seed.js                 # idempotent example data
├── static/favicon.svg
├── Dockerfile
├── docker-compose.yml
├── drizzle.config.ts
├── svelte.config.js
├── vite.config.ts
├── tailwind.config.ts (none — Tailwind v4 uses @theme in app.css)
├── tsconfig.json
└── package.json

How to post

After signing in (top-right → Sign in → Gitea), use:

  • + New in the nav (or /projects/new) — a project: title, summary, markdown body, links
  • /posts/new — a shorter update / write-up

Both are auth-gated. Anyone in RnD on Gitea can post.

Contributing

This is internal R&D — open a PR against main on gitea.wbd-rd.nl/RnD/helix.

License

Internal — Waterschap Brabantse Delta R&D.

Description
landing page R&D projects
Readme 195 KiB
Languages
Svelte 64.2%
TypeScript 23.3%
JavaScript 6.9%
CSS 3.8%
Dockerfile 1.2%
Other 0.6%