Skip to content
vanawarePublic

About

A blazing-fast, config-driven link-in-bio site generator. One config.json in, a static SEO-ready PWA out — built and served with pure Deno, deployable to GitHub Pages

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

@vanaware/linklist 🔗

A link-in-bio site generator: describe yourself in a single config.json and get a fast, accessible, private, static page you can host anywhere (GitHub Pages, Netlify, Vercel, Cloudflare Pages, Nginx…). Built on Deno, Preact + Signals, and BeerCSS (Material Design 3).

License: MIT Deno 2.x


🌟 Highlights

  • One file, whole page: everything — profile (identity + head metadata), the ordered blocks flow (links as rows/buttons/cards, Markdown sections optionally framed as cards, figures optionally framed as cards, and interactive islands), theme, footer — lives in a versioned config.json, validated with ArkType.
  • Static head + client-rendered body: the build bakes the per-profile SEO <head> (title / OG / Twitter / JSON-LD) and the PWA manifest.json into a real index.html, while linklist.js fetches ./config.json at runtime, applies the theme and renders the Preact app. Crawlers and link previews see the metadata without JS. Paths are relative, so it works at a domain root or a subfolder.
  • 100% Pure Deno: no Node runtime and no node_modules workflow. The static build is declarative via @vanaware/buildit (scripts/esbuild.jsonc / scripts/denobuild.jsonc); a small Cliffy CLI in core (packages/core/src/cli.ts) generates the site files and validates configs.
  • Pure BeerCSS + native theming: the whole page is BeerCSS markup. Brand skins (violet, indigo) selectable via config.theme.preset override only BeerCSS color tokens; dark/light come from BeerCSS's body.dark/body.light (mode: auto resolves without a flash).
  • Rich text where you want it: Markdown (CommonMark + GFM) in the bio, tagline, section bodies, link subtitles and footer — rendered to real Preact VNodes, no dangerouslySetInnerHTML.
  • Interactive islands: an island block hydrates a plain-JS Preact component from public/islands/ in place, reusing the app's single shared Preact/Signals instance — no build step, no second copy, and the page still works (static placeholder) if an island never loads.
  • Private by default: no analytics or click tracking — there is none to opt into.

🚀 Quick start

Requirements

Edit your config

Start from a template: docs/templates/minimal.config.json (bare-bones) or packages/core/public/config.json (the fully-annotated sample that powers the official site). The shape mirrors packages/core/src/lib/types.ts, with the JSON Schema at packages/core/schema/config.schema.json (published at https://vanaware.github.io/linklist/config.schema.json).

git clone https://github.com/vanaware/linklist.git
cd linklist

# Copy a starter and edit it
cp packages/core/public/config.json site/config.json

# Validate your config at any time
deno task validate-config site/config.json

Preview locally

deno task dev        # build, then serve packages/server/build/dist

Open the URL the server prints. (See Building for the sandbox fallback.)


🏗️ Build

The build is entirely driven by buildit config files — first deno task linklist:build regenerates index.html / manifest.json / robots.txt (SEO head baked in) into packages/core/public, then the bundler cleans the output dir, copies the static assets

  • theme CSS + config.json, and bundles core/src/entry.ts → linklist.js:
Task Engine Output Notes
deno task esbuild esbuild (preferred) packages/server/build/dist Dev bundle; runs linklist:build first.
deno task denobuild Deno in-process bundler packages/server/build/dist Fallback for sandboxes where esbuild's subprocess can't spawn.
deno task linklist:build core CLI (src/cli.ts) packages/core/public Site-file codegen only (chained by the tasks above).

⚠️ Local sandbox note: on some locked-down containers esbuild's npm subprocess cannot start (ChildProcess.unref undefined). There, use deno task denobuild (Deno's own in-process bundler) to produce linklist.js, then deno task dev. CI runners build with esbuild normally.

Everything above is wrapped by one command, which builds and then serves the output with the static server:

deno task dev        # denobuild + packages/server file-server (port from packages/server/.env)

🎨 Theming

{
  "theme": {
    "preset": "violet", // a brand skin CSS file from packages/core/themes
    "mode": "auto", // light | dark | auto
    "custom": { "primary_color": "#6750a4", "font_family": "Inter" } // BeerCSS token overrides
  }
}

A skin is a small CSS file in packages/core/themes that overrides only the BeerCSS color tokens (--primary, --on-primary, --primary-container, --on-primary-container, --font); linklist-base.css is always loaded for the shared layout. Add a skin by dropping a <name>.css in packages/core/themes — it is copied into the build automatically. A custom primary_color automatically derives a readable --on-primary ink; surfaces and elevation stay owned by BeerCSS per light/dark mode. The whole --ll-* token system was removed. See docs/theming.md.


📁 Repository structure

├── packages/
│   ├── core/                   # @vanaware/linklist (the engine)
│   │   ├── src/
│   │   │   ├── mod.ts          #   public API surface (side-effect-free)
│   │   │   ├── entry.ts        #   browser bootstrap -> bundled linklist.js
│   │   │   ├── cli.ts          #   build + validate CLI (Cliffy; the file-writing edge)
│   │   │   ├── version.ts      #   build-stamped APP_VERSION (generated)
│   │   │   ├── lib/            #   pure engine: types, schema, theme, seo, generate
│   │   │   └── components/     #   Preact UI + signals + markdown renderer
│   │   ├── schema/             #   config.schema.json (published for editors + the site)
│   │   ├── themes/             #   linklist-base.css + BeerCSS brand skins (violet, indigo)
│   │   └── public/             #   index.html shell, config.json, manifest, favicon, robots, islands/
│   └── server/                 # static file-server (serveDir) used for local preview
├── scripts/                    # buildit configs + export/version tooling (I/O edge)
├── .github/workflows/          # ci.yml (gate) + pages.yml (build & deploy)
└── deno.jsonc                  # workspace + tasks

🧪 Testing & verification

deno task tests      # check:all + lint + fmt:check + test (the full gate)
deno task test       # BDD tests (@std/testing/bdd + @std/assert) only

🌐 Deploy

  • GitHub Pages: push a v*.* tag (or run the workflow manually). pages.yml runs the gate, builds the site into packages/server/build/dist, and publishes it. The site uses relative paths, so a project-page subfolder works out of the box.
  • Any static host: run deno task esbuild (or denobuild) and upload packages/server/build/dist — it is fully self-contained.
  • Custom domain: point DNS at your host and add a CNAME; never hardcode base paths.

📚 Documentation

Full guides live in docs/:

Contributing? Read the architectural rules in AGENTS.md. For current status and the next milestone, see CURRENT.md.


📄 License

MIT © Vanaware

About

A blazing-fast, config-driven link-in-bio site generator. One config.json in, a static SEO-ready PWA out — built and served with pure Deno, deployable to GitHub Pages

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages