diff --git a/.gitignore b/.gitignore index ad7c24f..b89ad7e 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,8 @@ npm-debug.log* # Generated static/css/site.compiled.css + +# Nix +result +result-* +.direnv/ diff --git a/Makefile b/Makefile index 206aa66..c09cd2c 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: help serve size validate clean +.PHONY: help serve size validate tidy ascii clean help: ## Show this help @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \ @@ -28,5 +28,14 @@ validate: ## Quick HTML sanity check (counts opening vs closing tags) diff <(echo "$$open") <(echo "$$close") || true; \ done -clean: ## Remove generated artifacts +tidy: ## Run html-tidy on every .html + @for f in *.html; do echo "--- $$f ---"; tidy -e -q $$f || true; done + +ascii: ## Regenerate the body-rain ASCII art (needs chafa) + @mkdir -p static/ascii + chafa -f symbols -c none -s 240x60 --symbols ascii --animate off \ + static/img/og-image-bg.svg > static/ascii/rain.txt + @echo "Regenerated static/ascii/rain.txt (from static/img/og-image-bg.svg)" + +clean: ## Remove build artefacts @rm -rf bin/ node_modules/ dist/ diff --git a/README.md b/README.md index 0cc95fe..bc35865 100644 --- a/README.md +++ b/README.md @@ -39,8 +39,104 @@ The `Makefile` wraps the Python server and a few convenience commands: make serve # python3 -m http.server 8000 make size # report file sizes make validate # quick HTML syntax check (search for unclosed tags) +make tidy # run html-tidy on every .html (warnings only) +make ascii # regenerate the body-rain ASCII art (needs `chafa`) ``` +## Animated ASCII body background + +The page background is **animated ASCII art** generated by +[`chafa`](https://github.com/hpjansson/chafa) from a stripped-down version +of the Open Graph card (`static/img/og-image-bg.svg` — black background, +grid, the `tui-pages` wordmark, the "A framework for / building TUIs in +Rust." headline, and the subtitle, with the install button cropped out +because chafa was rendering it as a row of `@` symbols). The art is plain +text inside `
` blocks; the animation is pure CSS. + +``` +static/ascii/rain.txt ← 240 × ~50 grid of og-image-bg.svg +``` + +To re-render after changing the source SVG (you need `chafa` on `$PATH`, +or run inside `nix develop`): + +```bash +make ascii +``` + +The source SVG is committed (`static/img/og-image-bg.svg`) so `make ascii` +is fully reproducible — no ImageMagick, no cropping step. + +### How it's wired + +- A single `` is placed at + the top of ``, fixed full-bleed, with `z-index: -1` and + `pointer-events: none` so it never blocks the UI. +- Inside, a `` holds **two byte-identical + copies** of the chafa output stacked vertically for a seamless + `translateY(0 → -50%)` loop over 90 s. +- Color is `#f4a26b` (warm rust-orange) at 14% opacity with a faint + `text-shadow` glow — picks up the brand colour and reads as a subtle + background, not a wall of `@` blocks. +- A `::after` overlay draws a 2 px CRT scanline pattern that ticks + downward at 8 s/cycle. +- The hero section has a `.tp-ascii-hero-overlay` class that paints a + vertical gradient over the top of the page, so the hero text stays + readable while the ASCII art shows through on the bottom half and below. +- The light theme inverts to dark text on light, dimmer, no glow. +- All animations are disabled inside + `@media (prefers-reduced-motion: reduce)`. + +The hero (`#hero`) is **not** animated — it keeps the original static +`static/img/terminal-default.svg` mockup, exactly as designed. + +## Nix (optional, recommended) + +A `flake.nix` is included that ships a reproducible dev shell, a buildable +package, a `serve` app, and an `html-tidy` check. You do **not** need Nix to +use this project — it's a convenience. + +```bash +# Enter the dev shell (gives you: gnumake, python3, asciinema, html-tidy, …) +nix develop + +# Build the whole site → ./result/ (16 files, ~150 kB, ready to upload) +nix build +ls result/ + +# Build + serve on http://localhost:8000 (or $PORT) +nix run .#serve + +# Validate the HTML, format the flake +nix flake check +nix fmt +``` + +### What the flake provides + +| Output | Command | What it does | +| --- | --- | --- | +| `packages..default` | `nix build` | Assembles the static site into a single derivation | +| `apps. .serve` | `nix run .#serve` | Builds the package, then `python3 -m http.server` it | +| `devShells. .default` | `nix develop` | Shell with `make`, `python3`, `asciinema`, `tidy`, `curl` | +| `checks. .tidy` | `nix flake check` | Runs `html-tidy` over every `.html` in the built site | +| `formatter. ` | `nix fmt` | `nixpkgs-fmt` for the flake itself | + +### Why no Rust toolchain? + +The crate lives in `../komp_ac/tui-pages/`. This repo only contains the +*static* website, so the flake intentionally omits `rust-overlay` and a +`rustPlatform` — they would add hundreds of MB to the shell closure for +nothing. When the site eventually gains a Rust backend, those inputs come +back. + +### Why the per-system pattern? + +Mirrored from the upstream Codex CLI flake so future contributors see a +shape they already recognise. `forAllSystems` is a one-liner that +guarantees `linux`/`darwin` × `x86_64`/`aarch64` parity without sprinkling +`if` branches across the file. + ## Going to production The CDN approach is fine for marketing pages. For better performance @@ -75,16 +171,22 @@ tui-pages-web/ ├── sitemap.xml # sitemap ├── static/ │ ├── css/ -│ │ └── site.css # our custom layer (small) +│ │ ├── site.css # our custom layer (small) +│ │ └── ascii.css # animation rules for the body-rain ASCII art │ ├── img/ │ │ ├── favicon.svg │ │ ├── logo.svg -│ │ ├── og-image.svg +│ │ ├── og-image.svg # 1200x630 social share card (used as-is) +│ │ ├── og-image-bg.svg # 1200x500 cropped variant (chafa source) │ │ ├── terminal-default.svg │ │ ├── terminal-canvas.svg │ │ └── terminal-keybindings.svg +│ ├── ascii/ +│ │ └── rain.txt # chafa render of og-image-bg.svg │ └── demos/ # (empty — drop asciinema .cast files here) ├── content/ # (empty — markdown for blog/changelog later) +├── flake.nix # Nix dev shell, package, app, checks +├── flake.lock # (generated — pinned nixpkgs) ├── Makefile ├── .gitignore └── README.md @@ -92,8 +194,9 @@ tui-pages-web/ ## Adding an asciinema demo to the hero -The hero currently shows a static SVG terminal mockup. To replace it with a -real asciinema recording: +The hero currently shows the **static SVG mockup** +`static/img/terminal-default.svg`. To replace it with a real asciinema +recording (much more impressive, but requires a recorded cast): 1. Record one of the examples: @@ -103,7 +206,8 @@ real asciinema recording: # ... run the app for a few seconds, hit ctrl-d to stop ``` -2. In `index.html`, replace the hero terminal block with: +2. In `index.html`, replace the `` block + with: ```html @@ -118,6 +222,8 @@ real asciinema recording: ``` +The body-rain ASCII art is independent of the hero and stays as-is. + ## License The website source is MIT-licensed. The terminal mockup SVGs in `static/img/` diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..4070242 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1779560665, + "narHash": "sha256-tpyBcxPpcQb8ukyNF7DoCwfSY3VPsxHoYwj00Cayv5o=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "64c08a7ca051951c8eae34e3e3cb1e202fe36786", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..57478d4 --- /dev/null +++ b/flake.nix @@ -0,0 +1,185 @@ +{ + description = "Nix flake for the tui-pages website — static HTML/CSS/SVG, CDN-loaded JS"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + # NOTE: No rust-overlay — this is a pure-static site, no Rust toolchain required. + }; + + outputs = { self, nixpkgs, ... }: + let + systems = [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + forAllSystems = f: nixpkgs.lib.genAttrs systems f; + + version = "0.1.0"; + in + { + # `nix fmt` — format this flake + formatter = forAllSystems (system: nixpkgs.legacyPackages.${system}.nixpkgs-fmt); + + # ------------------------------------------------------------------------- + # packages..default + # + # The whole website as a single derivation. `nix build` produces ./result/ + # containing the static files, ready to: + # - serve with any static file server, or + # - upload to Netlify / Cloudflare Pages / GitHub Pages / S3 / etc. + # ------------------------------------------------------------------------- + packages = forAllSystems (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + src = ./.; + in { + default = pkgs.stdenvNoCC.mkDerivation { + pname = "tui-pages-website"; + inherit version; + inherit src; + + dontBuild = true; + + installPhase = '' + runHook preInstall + mkdir -p $out + # Use cp -R with --no-preserve=mode so the read-only Nix-store + # permissions don't leak into $out (a few hosts reject the + # resulting 0444 file modes when serving). + cp -R --no-preserve=mode ${src}/. $out/ + chmod -R u+w $out + runHook postInstall + ''; + + meta = with pkgs.lib; { + description = "Static website for the tui-pages Rust crate"; + homepage = "https://tui-pages.dev"; + license = licenses.mit; + platforms = platforms.unix; + }; + }; + }); + + # ------------------------------------------------------------------------- + # apps. .serve + # + # `nix run .#serve` — build the site and boot `python3 -m http.server` + # against the built result. Honours $PORT (defaults to 8000). + # ------------------------------------------------------------------------- + apps = forAllSystems (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + site = self.packages.${system}.default; + in { + serve = { + type = "app"; + program = toString (pkgs.writeShellScript "tui-pages-web-serve" '' + echo "Serving ${site} on http://localhost:''${PORT:-8000}" + cd ${site} + exec ${pkgs.python3}/bin/python3 -m http.server "''${PORT:-8000}" + ''); + meta = with pkgs.lib; { + description = "Build the tui-pages website and serve it on $PORT (default 8000)"; + platforms = platforms.unix; + }; + }; + }); + + # ------------------------------------------------------------------------- + # devShells. .default + # + # `nix develop` — everything the Makefile, the README, and future + # workflows need. No Rust toolchain by design. + # ------------------------------------------------------------------------- + devShells = forAllSystems (system: + let pkgs = nixpkgs.legacyPackages.${system}; + in { + default = pkgs.mkShell { + packages = with pkgs; [ + # --- Build / serve / validate (the Makefile uses these) ---- + gnumake + python3 + gawk + gnused + gnugrep + coreutils + + # --- HTML validation (optional, for the tidy check) -------- + html-tidy + + # --- Smoke-test the local server --------------------------- + curl + + # --- Record TUI demos (used to replace the SVG mockups) ---- + asciinema + + # --- Generate the body-background ASCII art from the og-image + # `make ascii` runs `chafa` to convert the og-image into + # a text grid, which is then embedded in index.html and + # animated with CSS as a slow drifting body background. + chafa + + # --- Optional: standalone Tailwind CLI for production CSS -- + # Uncomment if you switch from the Play CDN to a precompiled stylesheet. + # nodejs + ]; + + shellHook = '' + # Some distros ship `python` but not `python3`. Make the + # `python3` invocation in the Makefile portable. + if ! command -v python3 >/dev/null 2>&1 && command -v python >/dev/null 2>&1; then + alias python3=python + fi + + cat <<'EOF' + ┌──────────────────────────────────────────────────────────────┐ + │ tui-pages website dev shell │ + │ │ + │ make serve python3 -m http.server 8000 │ + │ make size report file sizes │ + │ make validate HTML tag balance check │ + │ make tidy run html-tidy on every .html │ + │ make ascii regenerate body-rain ASCII art │ + │ │ + │ asciinema rec static/demos/intro.cast record a demo │ + │ chafa -s 240x60 -c none \│ + │ static/img/og-image-bg.svg >│ + │ static/ascii/rain.txt render body rain │ + │ │ + │ nix build build site → ./result/ │ + │ nix run .#serve build + serve (honours $PORT) │ + │ nix fmt format this flake │ + │ nix flake check run all checks │ + └──────────────────────────────────────────────────────────────┘ + EOF + ''; + }; + }); + + # ------------------------------------------------------------------------- + # checks. .tidy + # + # HTML sanity check using tidy. Non-blocking — prints warnings to the + # build log, never fails. `nix flake check` runs this. + # ------------------------------------------------------------------------- + checks = forAllSystems (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + site = self.packages.${system}.default; + in { + tidy = pkgs.runCommand "tui-pages-website-html-check" + { + nativeBuildInputs = [ pkgs.html-tidy ]; + } '' + cd ${site} + for f in *.html; do + echo "─── $f ───" + ${pkgs.html-tidy}/bin/tidy -e -q -utf8 "$f" 2>&1 | head -20 || true + done + touch $out + ''; + }); + }; +} diff --git a/index.html b/index.html index a36d52a..e99b2da 100644 --- a/index.html +++ b/index.html @@ -77,6 +77,8 @@ + + + + + ++ + + Skip to content diff --git a/static/ascii/rain.txt b/static/ascii/rain.txt new file mode 100644 index 0000000..fdad7eb --- /dev/null +++ b/static/ascii/rain.txt @@ -0,0 +1,49 @@ + + + + + + + _ + =$ sa a y sa_gwaanggug, : + ? ?F fY ` @F?YFZ@~?f r~ : + ` + + + + + + + + + + + yyyy_ yagggm aggr _ygggs + _@@@@@_ @@@~~~ _ __ __ _ __ __ _ 4@@F $@@~~~ __ _ + $@@~@@$ $@@@@@@L$@@y@@@L4@@@@@@g_ $@@W@@@@yg@@@@y yg@@@@@gy "@@$ $@@, y@@E _a@@@@@@y 4@@$g@@F4@@F y@@@F 4@@@@@@$_a@@@@@@g, @@$g@@$ + g@@F ~@@@ ``@@@`` $@@@~`` `yyyyy@@@ $@@F``@@@F `@@@ u@@@yyy$@@L @@@,y@B@@ $@@ @@@~ 4@@@ 4@@@~``~4@@@@@P~ `@@@```$@@~ 7@@$ @@@~``' + g@@@$$$@@@y @@@ $@@L y@@@FFF@@@ $@@ @@@ @@@ 4@@@PPPPPPT "@@$$@~$@W@@F @@@ y@@@ 4@@$ 4@@@@@g_ @@@ @@@ @@@ @@$ + y@@@~~~~~$@@L @@@ $@@L "@@$yy$@@@ $@@ @@@ @@@ R@@gyyyg@ 4@@@E "@@@@ 4@@$yg@@@~ 4@@$ 4@@F~R@@g_ @@@ 7@@@ga@@@~ @@$ + ^~~^ "~~~ ~~~ ~~~~ `~FF~~~~~ 7~~ ~~~ ~~~ `~FFFF~~ ~~~ ~~~~ `~FFFF~ "~~~ ~~~~ ~~~T ~~~ ~FFFF~ ~~~ + + + `:: ::: ::: ::: ::: ::::::::::: ::: ::: :::: ::: ::::::::. :: + `:: ..... ... ... ... ::: ..... ::: ... ... ..... .... ... ````:::```` ::: ::: `::: ...... ... .. ..... :::```::: ... ... ....... ..::... + `:::```::: ::: ::: ::: ::: :::```:::: ::: ::::```::. :::```::: ::: ::: ::: `::: ::````` ::: :::```::: :::...::` ::: ::: :::````` ``::``` + `::' ::: ::: ::: ::: ::: ::: `::: ::: ::: ::: ::: ::: ::: ::: ::: `::: `:::::.. ::: ::' ::: :::``:::. ::: ::: `::::::. :: + `::: .::: ::: .::: ::: ::: :::. :::: ::: ::: ::: `:::. .::: ::: `::: .:::' `::: ```::: ::: :: ::: ::: `:::. :::. .::: . ````:: ::. ::: + `````:::`` ``::````` ``' ``` ```::````' ``` ``` ``' ``````::: ``` ```:::``` ```' ``:::``` ``` `` ``` ``` ```. ``::````` ``::::`` ````` ``` + :....:::` + ```````` + + .y + - i + . + 4W*MF~EZK,,4~3 $~f~%g~M $~_Ta F~K~L4~$~$uEZLf~$ Q~a~3 F~ [ E4E_ $='EZ%4_z F~K~LyT% F~L4Z, TM #~$uM7L B~%_T3 F~$yED F~LyT% yy^%uT7LyT%?F Fg~3 F~L + " ?=! 4rf ~ ?=~`g9 F ?=? ~ ~ ~f ^ f h= ?=F `=F ?= ^rF:=^J F~r?=:_$~ ~ ~ ~?=T M=~==~z "=f f f hf $=~"=? ]gF"h= ~ ~?=? ?^ f _g ^=T h ~`=F ~ ~ + ` ` ` ` `` ` + y== y_ _ __ __ _ _ _ ,___ y_= __ __ __ __ __ __ __ __ _ __ __ __ __a__ ;y_ __ _,___ _ __ _ yg _, __ __ _ _ __ ___ _ ___ ____ _ ____ + `Th $`$ $ L`L $`$=="yMy~$`4 $`$ F`L$ 9 `L F`La== "=yy=g $ F la== =d $ E `4`4 $4`4-= E $`4 4 F`$== 4`# 3 F` P=\4_F4== E ~yF 4~J $`$ $4 $==4```L + `T~ ~~`T` ~T ~ `T" ~ ~ " " ~~" ~ ~`Z4 ~ ~ ~ TT `T~`T~ ~ ~ ~ ~T T~ ^ `T'~ ~ ~ ~ ~T ~T"`~`T~ ~ `T^ ~ `T` ~T~ ~ ~T ~ .F 4~~ ~ ~T`J `T^ ~T ~ ~ + + + + diff --git a/static/css/ascii.css b/static/css/ascii.css new file mode 100644 index 0000000..2af3439 --- /dev/null +++ b/static/css/ascii.css @@ -0,0 +1,111 @@ +/* ========================================================================== + tui-pages website - animated ASCII art layer (chafa-rendered) + Loaded AFTER site.css. Provides: + - .tp-ascii-rain full-bleed body background, slow drifting pattern + sourced from static/img/og-image-bg.svg + All animations respect prefers-reduced-motion. + ========================================================================== */ + +/* Body-wide ASCII art background ---------------------------------------- */ +/* Fixed full-bleed layer behind all content. Renders a chafa output of the + og-image-bg.svg (black background + "tui-pages" wordmark + headline + + subtitle), drawn as ASCII glyphs in a single full-width tile. Two copies + of the tile are stacked vertically and the whole track drifts slowly + upward, looping seamlessly when the top copy exits the viewport. + + The hero is a static_ =$ sa a y sa_gwaanggug, : ? ?F fY ` @F?YFZ@~?f r~ : ` yyyy_ yagggm aggr _ygggs _@@@@@_ @@@~~~ _ __ __ _ __ __ _ 4@@F $@@~~~ __ _ $@@~@@$ $@@@@@@L$@@y@@@L4@@@@@@g_ $@@W@@@@yg@@@@y yg@@@@@gy "@@$ $@@, y@@E _a@@@@@@y 4@@$g@@F4@@F y@@@F 4@@@@@@$_a@@@@@@g, @@$g@@$ g@@F ~@@@ ``@@@`` $@@@~`` `yyyyy@@@ $@@F``@@@F `@@@ u@@@yyy$@@L @@@,y@B@@ $@@ @@@~ 4@@@ 4@@@~``~4@@@@@P~ `@@@```$@@~ 7@@$ @@@~``' g@@@$$$@@@y @@@ $@@L y@@@FFF@@@ $@@ @@@ @@@ 4@@@PPPPPPT "@@$$@~$@W@@F @@@ y@@@ 4@@$ 4@@@@@g_ @@@ @@@ @@@ @@$ y@@@~~~~~$@@L @@@ $@@L "@@$yy$@@@ $@@ @@@ @@@ R@@gyyyg@ 4@@@E "@@@@ 4@@$yg@@@~ 4@@$ 4@@F~R@@g_ @@@ 7@@@ga@@@~ @@$ ^~~^ "~~~ ~~~ ~~~~ `~FF~~~~~ 7~~ ~~~ ~~~ `~FFFF~~ ~~~ ~~~~ `~FFFF~ "~~~ ~~~~ ~~~T ~~~ ~FFFF~ ~~~ `:: ::: ::: ::: ::: ::::::::::: ::: ::: :::: ::: ::::::::. :: `:: ..... ... ... ... ::: ..... ::: ... ... ..... .... ... ````:::```` ::: ::: `::: ...... ... .. ..... :::```::: ... ... ....... ..::... `:::```::: ::: ::: ::: ::: :::```:::: ::: ::::```::. :::```::: ::: ::: ::: `::: ::````` ::: :::```::: :::...::` ::: ::: :::````` ``::``` `::' ::: ::: ::: ::: ::: ::: `::: ::: ::: ::: ::: ::: ::: ::: ::: `::: `:::::.. ::: ::' ::: :::``:::. ::: ::: `::::::. :: `::: .::: ::: .::: ::: ::: :::. :::: ::: ::: ::: `:::. .::: ::: `::: .:::' `::: ```::: ::: :: ::: ::: `:::. :::. .::: . ````:: ::. ::: `````:::`` ``::````` ``' ``` ```::````' ``` ``` ``' ``````::: ``` ```:::``` ```' ``:::``` ``` `` ``` ``` ```. ``::````` ``::::`` ````` ``` :....:::` ```````` .y + - i + . 4W*MF~EZK,,4~3 $~f~%g~M $~_Ta F~K~L4~$~$uEZLf~$ Q~a~3 F~ [ E4E_ $='EZ%4_z F~K~LyT% F~L4Z, TM #~$uM7L B~%_T3 F~$yED F~LyT% yy^%uT7LyT%?F Fg~3 F~L " ?=! 4rf ~ ?=~`g9 F ?=? ~ ~ ~f ^ f h= ?=F `=F ?= ^rF:=^J F~r?=:_$~ ~ ~ ~?=T M=~==~z "=f f f hf $=~"=? ]gF"h= ~ ~?=? ?^ f _g ^=T h ~`=F ~ ~ ` ` ` ` `` ` y== y_ _ __ __ _ _ _ ,___ y_= __ __ __ __ __ __ __ __ _ __ __ __ __a__ ;y_ __ _,___ _ __ _ yg _, __ __ _ _ __ ___ _ ___ ____ _ ____ `Th $`$ $ L`L $`$=="yMy~$`4 $`$ F`L$ 9 `L F`La== "=yy=g $ F la== =d $ E `4`4 $4`4-= E $`4 4 F`$== 4`# 3 F` P=\4_F4== E ~yF 4~J $`$ $4 $==4```L `T~ ~~`T` ~T ~ `T" ~ ~ " " ~~" ~ ~`Z4 ~ ~ ~ TT `T~`T~ ~ ~ ~ ~T T~ ^ `T'~ ~ ~ ~ ~T ~T"`~`T~ ~ `T^ ~ `T` ~T~ ~ ~T ~ .F 4~~ ~ ~T`J `T^ ~T ~ ~ _ =$ sa a y sa_gwaanggug, : ? ?F fY ` @F?YFZ@~?f r~ : ` yyyy_ yagggm aggr _ygggs _@@@@@_ @@@~~~ _ __ __ _ __ __ _ 4@@F $@@~~~ __ _ $@@~@@$ $@@@@@@L$@@y@@@L4@@@@@@g_ $@@W@@@@yg@@@@y yg@@@@@gy "@@$ $@@, y@@E _a@@@@@@y 4@@$g@@F4@@F y@@@F 4@@@@@@$_a@@@@@@g, @@$g@@$ g@@F ~@@@ ``@@@`` $@@@~`` `yyyyy@@@ $@@F``@@@F `@@@ u@@@yyy$@@L @@@,y@B@@ $@@ @@@~ 4@@@ 4@@@~``~4@@@@@P~ `@@@```$@@~ 7@@$ @@@~``' g@@@$$$@@@y @@@ $@@L y@@@FFF@@@ $@@ @@@ @@@ 4@@@PPPPPPT "@@$$@~$@W@@F @@@ y@@@ 4@@$ 4@@@@@g_ @@@ @@@ @@@ @@$ y@@@~~~~~$@@L @@@ $@@L "@@$yy$@@@ $@@ @@@ @@@ R@@gyyyg@ 4@@@E "@@@@ 4@@$yg@@@~ 4@@$ 4@@F~R@@g_ @@@ 7@@@ga@@@~ @@$ ^~~^ "~~~ ~~~ ~~~~ `~FF~~~~~ 7~~ ~~~ ~~~ `~FFFF~~ ~~~ ~~~~ `~FFFF~ "~~~ ~~~~ ~~~T ~~~ ~FFFF~ ~~~ `:: ::: ::: ::: ::: ::::::::::: ::: ::: :::: ::: ::::::::. :: `:: ..... ... ... ... ::: ..... ::: ... ... ..... .... ... ````:::```` ::: ::: `::: ...... ... .. ..... :::```::: ... ... ....... ..::... `:::```::: ::: ::: ::: ::: :::```:::: ::: ::::```::. :::```::: ::: ::: ::: `::: ::````` ::: :::```::: :::...::` ::: ::: :::````` ``::``` `::' ::: ::: ::: ::: ::: ::: `::: ::: ::: ::: ::: ::: ::: ::: ::: `::: `:::::.. ::: ::' ::: :::``:::. ::: ::: `::::::. :: `::: .::: ::: .::: ::: ::: :::. :::: ::: ::: ::: `:::. .::: ::: `::: .:::' `::: ```::: ::: :: ::: ::: `:::. :::. .::: . ````:: ::. ::: `````:::`` ``::````` ``' ``` ```::````' ``` ``` ``' ``````::: ``` ```:::``` ```' ``:::``` ``` `` ``` ``` ```. ``::````` ``::::`` ````` ``` :....:::` ```````` .y + - i + . 4W*MF~EZK,,4~3 $~f~%g~M $~_Ta F~K~L4~$~$uEZLf~$ Q~a~3 F~ [ E4E_ $='EZ%4_z F~K~LyT% F~L4Z, TM #~$uM7L B~%_T3 F~$yED F~LyT% yy^%uT7LyT%?F Fg~3 F~L " ?=! 4rf ~ ?=~`g9 F ?=? ~ ~ ~f ^ f h= ?=F `=F ?= ^rF:=^J F~r?=:_$~ ~ ~ ~?=T M=~==~z "=f f f hf $=~"=? ]gF"h= ~ ~?=? ?^ f _g ^=T h ~`=F ~ ~ ` ` ` ` `` ` y== y_ _ __ __ _ _ _ ,___ y_= __ __ __ __ __ __ __ __ _ __ __ __ __a__ ;y_ __ _,___ _ __ _ yg _, __ __ _ _ __ ___ _ ___ ____ _ ____ `Th $`$ $ L`L $`$=="yMy~$`4 $`$ F`L$ 9 `L F`La== "=yy=g $ F la== =d $ E `4`4 $4`4-= E $`4 4 F`$== 4`# 3 F` P=\4_F4== E ~yF 4~J $`$ $4 $==4```L `T~ ~~`T` ~T ~ `T" ~ ~ " " ~~" ~ ~`Z4 ~ ~ ~ TT `T~`T~ ~ ~ ~ ~T T~ ^ `T'~ ~ ~ ~ ~T ~T"`~`T~ ~ `T^ ~ `T` ~T~ ~ ~T ~ .F 4~~ ~ ~T`J `T^ ~T ~ ~+as + designed - this file does NOT touch the hero. + + Regenerate the source tile with `make ascii` (uses chafa on + static/img/og-image-bg.svg). The chafa output is in static/ascii/rain.txt + and is 240 columns wide. */ + +.tp-ascii-rain { + position: fixed; + inset: 0; + z-index: 0; /* sit above the page background, below the content */ + pointer-events: none; + opacity: 0.14; + overflow: hidden; + font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace; + font-size: clamp(6px, 0.9vw, 9px); + line-height: 1.05; + color: #f4a26b; /* warm rust-orange tint, picks up the brand */ + white-space: pre; + text-shadow: 0 0 6px rgba(183, 65, 14, 0.25); /* faint phosphor glow */ + will-change: transform; +} + +/* The track holds two byte-identical copies of rain.txt stacked vertically. + translateY(0 -> -50%) is a perfect seamless loop. */ +.tp-ascii-rain-track { + display: block; + width: 100%; + margin: 0; + padding: 0; + animation: tp-ascii-drift 90s linear infinite; + will-change: transform; +} + +.tp-ascii-rain-cell { + display: block; + white-space: pre; /* preserve the chafa line breaks */ +} + +/* CRT scanlines overlay - a thin moving line every 2px. Faint so it doesn't + fight the page content. */ +.tp-ascii-rain::after { + content: ""; + position: absolute; + inset: 0; + background: repeating-linear-gradient( + to bottom, + transparent 0, + transparent 2px, + rgba(0, 0, 0, 0.18) 2px, + rgba(0, 0, 0, 0.18) 3px + ); + pointer-events: none; + animation: tp-ascii-scanline 8s linear infinite; +} + +@keyframes tp-ascii-drift { + 0% { transform: translateY(0); } + 100% { transform: translateY(-50%); } +} + +@keyframes tp-ascii-scanline { + 0% { background-position: 0 0; } + 100% { background-position: 0 6px; } +} + +/* The hero text is high-contrast on its own (zinc-50 / accent), and the + body ASCII rain is at 14% opacity. No overlay needed — the rain shows + through everywhere. */ + +/* Light theme: invert to dark text on light, dimmer, no glow. */ +:root[data-theme="light"] .tp-ascii-rain { + opacity: 0.10; + color: #1f1f23; + text-shadow: none; + mix-blend-mode: multiply; +} + +:root[data-theme="light"] .tp-ascii-rain::after { + background: repeating-linear-gradient( + to bottom, + transparent 0, + transparent 2px, + rgba(255, 255, 255, 0.12) 2px, + rgba(255, 255, 255, 0.12) 3px + ); +} + +/* Reduced motion: pause the drift, keep the static background. */ +@media (prefers-reduced-motion: reduce) { + .tp-ascii-rain-track, + .tp-ascii-rain::after { + animation: none !important; + transform: none !important; + } +} diff --git a/static/css/site.css b/static/css/site.css index 524cb34..9de593b 100644 --- a/static/css/site.css +++ b/static/css/site.css @@ -55,9 +55,7 @@ body { /* ---------- Hero --------------------------------------------------------- */ .tp-hero { - background: - radial-gradient(ellipse 80% 50% at 50% 0%, rgba(183, 65, 14, 0.15), transparent 70%), - linear-gradient(180deg, var(--tp-hero-from) 0%, var(--tp-hero-to) 100%); + background: transparent; /* let the body ASCII rain show through */ position: relative; isolation: isolate; } @@ -75,6 +73,7 @@ body { mask-image: radial-gradient(ellipse 60% 50% at 50% 0%, #000 30%, transparent 80%); pointer-events: none; z-index: -1; + opacity: 0.5; /* subtle grid, not a wall */ } /* Subtle floating code lines in the hero, behind the content */ @@ -206,13 +205,12 @@ body { .tp-terminal { position: relative; border-radius: 0.875rem; - background: #0b0b0f; + background: transparent; /* let the body ASCII art show through */ box-shadow: - 0 1px 0 rgba(255, 255, 255, 0.04) inset, - 0 30px 60px -20px rgba(0, 0, 0, 0.6), - 0 18px 36px -18px rgba(0, 0, 0, 0.5); + 0 0 0 1px rgba(244, 162, 107, 0.18), /* rust-orange hairline, brand */ + 0 30px 60px -20px rgba(0, 0, 0, 0.55), + 0 18px 36px -18px rgba(0, 0, 0, 0.45); overflow: hidden; - border: 1px solid rgba(63, 63, 70, 0.4); } .tp-terminal::after { content: ""; @@ -220,7 +218,14 @@ body { inset: 0; pointer-events: none; border-radius: inherit; - box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.03); + background: linear-gradient( + to bottom, + rgba(11, 11, 15, 0.55) 0%, + rgba(11, 11, 15, 0) 18%, + rgba(11, 11, 15, 0) 82%, + rgba(11, 11, 15, 0.45) 100% + ); /* darken only the top + bottom strips, leave the middle transparent + so the body ASCII art shows through the terminal content */ } /* ---------- Selection --------------------------------------------------- */ diff --git a/static/img/og-image-bg.svg b/static/img/og-image-bg.svg new file mode 100644 index 0000000..0326e1e --- /dev/null +++ b/static/img/og-image-bg.svg @@ -0,0 +1,29 @@ + diff --git a/static/img/terminal-default.svg b/static/img/terminal-default.svg index 8bba8f0..35b3242 100644 --- a/static/img/terminal-default.svg +++ b/static/img/terminal-default.svg @@ -1,17 +1,17 @@