Files
mifi-links/README.md
2026-02-06 19:09:48 -03:00

158 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# mifi.dev landing
One-page static Linktree-style site for [mifi.dev](https://mifi.dev). Built with SvelteKit (static adapter), TypeScript, pnpm, PostCSS, and critical CSS.
## Prerequisites
- **Node.js** 22+
- **pnpm** (enable via `corepack enable && corepack prepare pnpm@latest --activate`)
## Quick start
### Option A: Dev container (recommended)
Use the same Linux environment as CI so Playwright snapshots match.
1. Open the repo in VS Code or Cursor.
2. When prompted, click **Reopen in Container** (or run **Dev Containers: Reopen in Container** from the command palette).
3. Wait for the container to build; **postCreateCommand** runs `pnpm install` and `pnpm exec playwright install chromium --with-deps` so dependencies and the Playwright browser are ready.
4. Run the scripts below inside the container.
### Option B: Local
```bash
pnpm install
pnpm dev # dev server at http://localhost:5173
pnpm build # output in build/
pnpm preview # preview build at http://localhost:4173
```
## Scripts
| Script | Description |
| -------------------- | ------------------------------------------------------------------------------------------------ |
| `pnpm dev:bio` | Start Vite dev server for mifi.bio |
| `pnpm dev:dev` | Start Vite dev server for mifi.dev |
| `pnpm build` | Build static site to `build/` |
| `pnpm build:full` | Build + inline critical CSS (run `pnpm run critical-css:install` once to install Chromium) |
| `pnpm critical-css:install` | Install Chromium for critical CSS (required once before first `build:full`) |
| `pnpm preview` | Serve `build/` locally |
| `pnpm check` | Run `svelte-kit sync` and `svelte-check` |
| `pnpm lint` | ESLint + Stylelint |
| `pnpm format` | Prettier (write) |
| `pnpm format:check` | Prettier (check only) |
| `pnpm test` | Vitest (watch) |
| `pnpm test:run` | Vitest (single run) |
| `pnpm test:coverage` | Vitest with coverage |
| `pnpm test:e2e` | Playwright e2e (starts preview, then runs tests) |
| `pnpm test:e2e:ui` | Playwright e2e in UI mode |
## Project layout
- `src/` SvelteKit app (routes, layout, global CSS)
- `src/lib/data/` JSON content (e.g. `links.json`) loaded at build time
- `static/` Static assets
- `scripts/` Post-build scripts (e.g. critical CSS)
- `e2e/` Playwright e2e tests
- `build/` Output after `pnpm build` (gitignored)
## Tooling
- **Package manager:** pnpm
- **Language:** TypeScript
- **Lint:** ESLint, Stylelint
- **Format:** Prettier
- **Unit tests:** Vitest
- **E2E / visual regression:** Playwright (use same Linux build in dev container and CI)
- **Critical CSS:** Post-build step via `critical` (run `pnpm run critical-css:install` once, then `pnpm build:full`)
## Build and run with Docker
One image contains both variants (mifi.dev and mifi.bio). The Dockerfile runs two SvelteKit builds (`CONTENT_VARIANT=dev` and `CONTENT_VARIANT=bio`) and nginx serves by host.
```bash
docker build -t mifi-links:local .
docker run --rm -p 8080:80 mifi-links:local
```
Then open `http://localhost:8080` with `Host: mifi.dev` or `Host: mifi.bio` (e.g. add `127.0.0.1 mifi.dev` to `/etc/hosts` and visit `http://mifi.dev:8080`).
For production, the Portainer stack uses the image from the package registry. To run the stack locally with the built image, use the same `docker-compose.yml` and either point it at your local tag or run `docker compose build` then `docker compose up`.
## Deploy
- Repo: `git.mifi.dev/mifi-holdings/mifi-links`
- One image (both mifi.dev and mifi.bio); deploy via webhook to Portainer stack.
- `docker-compose.yml` defines one service with nginx and Traefik labels for `mifi.dev`, `www.mifi.dev`, `mifi.bio`, and `www.mifi.bio` on network `marina-net`.
## Fonts
Fonts live in `static/assets/fonts/`. The wordmark uses **Plus Jakarta Sans** (700). The self-hosted “latin” woff2 subsets omit ligature (GSUB) tables, so the **fi** ligature in “mifi” does not appear with them.
**Current setup:** Plus Jakarta Sans 700 is loaded from **Google Fonts** in the layout so the wordmark fi ligature works. All other fonts (Fraunces, Inter, etc.) are self-hosted.
**To self-host the wordmark font with ligatures** instead of Google Fonts:
1. **Download the font**
[Google Fonts → Plus Jakarta Sans](https://fonts.google.com/specimen/Plus+Jakarta+Sans) → “Download family” (ZIP). Unzip; use the **Bold** static file (e.g. `PlusJakartaSans-Bold.ttf` or `PlusJakartaText-Bold.otf` from the `static` folder). Or clone [Tokotype/PlusJakartaSans](https://github.com/tokotype/PlusJakartaSans) and use `fonts/ttf/PlusJakartaSans-Bold.ttf` (or the OTF equivalent).
2. **Subset with ligatures** (from the repo root, with fonttools installed: `pip install fonttools brotli`):
```bash
# Replace PATH_TO_BOLD with the path to the Bold TTF/OTF (e.g. ~/Downloads/Plus_Jakarta_Sans/static/PlusJakartaSans-Bold.ttf)
# Use --layout-features='*' to keep all layout features (including liga); or 'liga','clig'
pyftsubset PATH_TO_BOLD \
--output-file=static/assets/fonts/plus-jakarta-sans-700-liga.woff2 \
--flavor=woff2 \
--layout-features='*' \
--unicodes='U+0020-007F,U+00A0-00FF,U+FB01,U+FB02'
```
Example if the ZIP is in your Downloads folder:
```bash
pyftsubset ~/Downloads/Plus_Jakarta_Sans/static/PlusJakartaSans-Bold.ttf \
--output-file=static/assets/fonts/plus-jakarta-sans-700-liga.woff2 \
--flavor=woff2 \
--layout-features='*' \
--unicodes='U+0020-007F,U+00A0-00FF,U+FB01,U+FB02'
```
3. **Switch back to self-hosted**
Put the new woff2 in `static/assets/fonts/`. In `src/lib/fonts.css`, uncomment the Plus Jakarta Sans `@font-face` and set its `url()` to `plus-jakarta-sans-700-liga.woff2`. In `src/routes/+layout.svelte`, remove the Google Fonts `<link>` for Plus Jakarta Sans.
### Fraunces variable (headings with opsz)
Headings use **Fraunces** with `font-variation-settings: "opsz" 36`. That only works with the **variable** font (opsz + wght axes). Static instances (e.g. `fraunces-v38-latin-500.woff2`) ignore opsz.
1. **Download the variable TTF**
[Google Fonts → Fraunces](https://fonts.google.com/specimen/Fraunces) → “Download family”. In the ZIP, use the file in the **variable** folder: `Fraunces-VariableFont_opsz,wght.ttf` (or similar name).
2. **Subset to woff2** (from repo root; `pip install fonttools brotli`):
```bash
# Replace PATH_TO_VARIANT with the path to Fraunces-VariableFont_opsz,wght.ttf
pyftsubset PATH_TO_VARIANT \
--output-file=static/assets/fonts/fraunces-variable-opsz-wght.woff2 \
--flavor=woff2 \
--layout-features='*' \
--unicodes='U+0020-007F,U+00A0-00FF'
```
Example if the ZIP is in Downloads:
```bash
pyftsubset ~/Downloads/Fraunces/fraunces-variable-opsz-wght.ttf \
--output-file=static/assets/fonts/fraunces-variable-opsz-wght.woff2 \
--flavor=woff2 \
--layout-features='*' \
--unicodes='U+0020-007F,U+00A0-00FF'
```
3. **Use it in the app**
In `src/lib/fonts.css`, replace the Fraunces `@font-face` with one that points at the variable woff2 and a `font-weight` range (e.g. 100 900) so the browser can use the wght axis. See the comment in `fonts.css` for the exact block.
## CSP
CSP is set via Traefik middleware, not in app code.