How this site is built
This portfolio is itself a demonstration. The brief was a BI dashboard about a CV — so the tool of choice is Evidence.dev, an open-source BI-as-code framework where pages are Markdown with embedded SQL, and the artefact of a build is a static site.
The cloud-native bit
The data layer is Parquet files, generated by Python scripts. There is no live database. DuckDB reads the parquet at build time through Evidence's source layer, executes every SQL query in every page, and writes the answer as a static JSON next to the page that uses it.
scripts/*.py ──► /data/*.parquet ──► DuckDB (in-process, at build) ──► /build/static
That means:
- No runtime database: The served site has no backend. There is nothing to scale, nothing to page, nothing to patch.
- Reproducible builds: Re-running
python scripts/build_cv_parquet.pythennpm run buildgives identical artefacts with fresh source data. - Cost-efficient: Hosting is an object bucket. Compute is whichever CI runner executes the build.
Why Evidence
Evidence's query results are cached at build. Authoring still feels like a BI tool — sql blocks (hidden from you). The same pattern you would build with static rendering, except the authoring grammar is "data-first" rather than "component-first."
There is however a big trade-off: pages can't query live data; the site is as fresh as its last build. For a CV-as-dashboard that is exactly correct. The grids pages are mainly impacted by this, so they require a scheduled rebuild to get the latest data from the external data sources.
The geospatial bit
The Grids pages host real geometries on a MapLibre GL map. The pipeline:
- Source. OpenStreetMap or domain-specific open data (e.g. Elia's open dataset for the HV grid) is pulled into Parquet by Python scripts.
- Shape. A build-time step applies the relevant filters, splits concatenated values where needed (e.g.
voltage='220000;150000'), and assembles human-readable popup attributes. Output: GeoJSON, served as plain static files. - Basemap. A Protomaps PMTiles archive provides the basemap — single-file vector tile archive, range-requested from the bucket, no tile server!
- Overlay. MapLibre GL renders the GeoJSON with data-driven paint expressions (voltage → line width, location → dashed/solid).
The "no runtime backend" property holds end-to-end: the bucket is the deployment target, the build is the only thing that touches a database or API request, and the map renders fully client-side.
Why Scaleway
The hosting target is a Scaleway Object Storage bucket with static-site serving. EU-resident, transparent pricing and modern services. Build output is synced into the bucket. The build itself runs on Scaleway Serverless Jobs to refresh the underlying data at a regular interval.
What's interesting here
The pitch is that a static, BI-as-code site is a good fit for many "data tells the story" use cases that are reflexively built as React apps over an API. The boundary lives at the parquet file. Move the boundary, and you can move the entire serving stack to just a simple bucket.