From 876183a1ba073ecb453819ca9e95703dbb61397b Mon Sep 17 00:00:00 2001 From: Sarah Dayan <5370675+sarahdayan@users.noreply.github.com> Date: Sat, 21 Feb 2026 17:54:42 +0100 Subject: [PATCH 1/2] feat: add portless for stable dev server URLs Replace ephemeral port numbers with stable named .localhost URLs, eliminating port conflicts when running multiple examples simultaneously. Co-Authored-By: Claude Opus 4.6 --- apps/resolver/package.json | 2 +- examples/js/package.json | 2 +- examples/js/vite.config.js | 17 +++++++++++++++++ examples/react/package.json | 2 +- examples/react/vite.config.ts | 21 ++++++++++++++++++++- package-lock.json | 34 ++++++++++++++++++++++++++++++++++ package.json | 1 + 7 files changed, 75 insertions(+), 4 deletions(-) diff --git a/apps/resolver/package.json b/apps/resolver/package.json index 2e2a9d4..45cb8c7 100644 --- a/apps/resolver/package.json +++ b/apps/resolver/package.json @@ -3,7 +3,7 @@ "version": "0.0.0", "private": true, "scripts": { - "dev": "wrangler dev", + "dev": "portless resolver sh -c 'wrangler dev --port $PORT'", "deploy": "wrangler deploy", "test": "vitest run", "test:watch": "vitest" diff --git a/examples/js/package.json b/examples/js/package.json index 6f6fb65..433b91b 100644 --- a/examples/js/package.json +++ b/examples/js/package.json @@ -2,7 +2,7 @@ "name": "@experiences/example-js", "private": true, "scripts": { - "dev": "vite", + "dev": "PORTLESS_NAME=example-js portless example-js vite", "build": "vite build", "preview": "vite preview" }, diff --git a/examples/js/vite.config.js b/examples/js/vite.config.js index a9fd40c..b5479dd 100644 --- a/examples/js/vite.config.js +++ b/examples/js/vite.config.js @@ -38,6 +38,11 @@ function resolveFile(urlPath) { const pages = ['index.html', 'search.html', 'product.html']; export default { + server: { + host: '127.0.0.1', + port: process.env.PORT ? Number(process.env.PORT) : undefined, + strictPort: Boolean(process.env.PORT), + }, build: { rollupOptions: { input: Object.fromEntries( @@ -46,6 +51,18 @@ export default { }, }, plugins: [ + { + name: 'portless-url', + configureServer(server) { + const name = process.env.PORTLESS_NAME; + if (!name) return; + const port = process.env.PORTLESS_PORT || 1355; + server.printUrls = () => { + const url = `http://${name}.localhost:${port}/`; + server.config.logger.info(` ➜ Portless: ${url}`); + }; + }, + }, { name: 'local-canary-proxy', // Rewrite GitHub canary URLs to a local prefix so Vite serves them. diff --git a/examples/react/package.json b/examples/react/package.json index 8a6f761..05e9352 100644 --- a/examples/react/package.json +++ b/examples/react/package.json @@ -2,7 +2,7 @@ "name": "@experiences/example-react", "private": true, "scripts": { - "dev": "vite", + "dev": "PORTLESS_NAME=example-react portless example-react vite", "build": "vite build", "preview": "vite preview" }, diff --git a/examples/react/vite.config.ts b/examples/react/vite.config.ts index 146fc22..1c6693f 100644 --- a/examples/react/vite.config.ts +++ b/examples/react/vite.config.ts @@ -2,7 +2,26 @@ import react from '@vitejs/plugin-react'; import { defineConfig } from 'vite'; export default defineConfig({ - plugins: [react()], + server: { + host: '127.0.0.1', + port: process.env.PORT ? Number(process.env.PORT) : undefined, + strictPort: Boolean(process.env.PORT), + }, + plugins: [ + { + name: 'portless-url', + configureServer(server) { + const name = process.env.PORTLESS_NAME; + if (!name) return; + const port = process.env.PORTLESS_PORT || 1355; + server.printUrls = () => { + const url = `http://${name}.localhost:${port}/`; + server.config.logger.info(` ➜ Portless: ${url}`); + }; + }, + }, + react(), + ], build: { outDir: 'build', }, diff --git a/package-lock.json b/package-lock.json index a612f44..e05dee0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "husky": "^9.1.7", "lint-staged": "^16.2.7", "oxlint": "^1.43.0", + "portless": "^0.4.0", "prettier": "^3.7.4", "turbo": "^2.8.3", "typescript": "5.9.2" @@ -4456,6 +4457,19 @@ "node": ">=18" } }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/check-error": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", @@ -6420,6 +6434,26 @@ "node": ">=0.10" } }, + "node_modules/portless": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/portless/-/portless-0.4.0.tgz", + "integrity": "sha512-CW3ERwHux/9VNrnvaSXa4sCtNlY300b5sUh6A52IBkpYMfJm7dMS4NipRVaSMFwyLAwFSp+OxGxpRD0zItiBWg==", + "dev": true, + "license": "Apache-2.0", + "os": [ + "darwin", + "linux" + ], + "dependencies": { + "chalk": "^5.3.0" + }, + "bin": { + "portless": "dist/cli.js" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", diff --git a/package.json b/package.json index 7d5a8c6..75c3737 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "husky": "^9.1.7", "lint-staged": "^16.2.7", "oxlint": "^1.43.0", + "portless": "^0.4.0", "prettier": "^3.7.4", "turbo": "^2.8.3", "typescript": "5.9.2" From f345ba090406628f08899f651f0cdf745cea39ef Mon Sep 17 00:00:00 2001 From: Sarah Dayan <5370675+sarahdayan@users.noreply.github.com> Date: Sun, 22 Feb 2026 11:31:23 +0100 Subject: [PATCH 2/2] chore: remove tech-badge pills from examples URLs are now legible thanks to portless, making the badges redundant. Co-Authored-By: Claude Opus 4.6 --- examples/js/index.html | 2 -- examples/js/product.html | 2 -- examples/js/search.html | 2 -- examples/js/styles.css | 25 +++++-------------------- examples/react/src/App.tsx | 2 -- 5 files changed, 5 insertions(+), 28 deletions(-) diff --git a/examples/js/index.html b/examples/js/index.html index 05b8237..3b66054 100644 --- a/examples/js/index.html +++ b/examples/js/index.html @@ -308,8 +308,6 @@

Shop by Category

- - diff --git a/examples/js/product.html b/examples/js/product.html index afa102f..a8e011a 100644 --- a/examples/js/product.html +++ b/examples/js/product.html @@ -243,8 +243,6 @@

Frequently Bought Together

-
Vanilla JS
- diff --git a/examples/js/search.html b/examples/js/search.html index 86b2b95..71d005d 100644 --- a/examples/js/search.html +++ b/examples/js/search.html @@ -199,8 +199,6 @@ -
Vanilla JS
- diff --git a/examples/js/styles.css b/examples/js/styles.css index 387d592..63a35b0 100644 --- a/examples/js/styles.css +++ b/examples/js/styles.css @@ -13,7 +13,7 @@ html { } body { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; color: #1a1a1a; background: #ffffff; line-height: 1.6; @@ -298,7 +298,9 @@ ul { overflow: hidden; background: #ffffff; border: 1px solid #e5e7eb; - transition: box-shadow 0.2s, transform 0.2s; + transition: + box-shadow 0.2s, + transform 0.2s; } .product-card:hover { @@ -693,23 +695,6 @@ ul { width: 380px; } -/* ===== TECH BADGE ===== */ -.tech-badge { - position: fixed; - bottom: 16px; - left: 16px; - z-index: 200; - padding: 5px 12px; - font-size: 12px; - font-weight: 700; - letter-spacing: 0.04em; - color: #1a1a1a; - background: #ffffff; - border: 1px solid #d1d5db; - border-radius: 999px; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); -} - /* ===== RESPONSIVE ===== */ @media (max-width: 1024px) { .hero-inner { @@ -830,4 +815,4 @@ ul { width: 60px; height: 60px; } -} \ No newline at end of file +} diff --git a/examples/react/src/App.tsx b/examples/react/src/App.tsx index 81d4e42..368599b 100644 --- a/examples/react/src/App.tsx +++ b/examples/react/src/App.tsx @@ -202,8 +202,6 @@ function Layout({ preview = false }: { preview?: boolean }) {
- -
React
); }