Open-source Vue 3 SPA that displays a continuously scrolling horizontal message, ideal for use as a banner on screens or mobile devices.
Live demo: banner.pauloklaus.com.br
Repository: github.com/pauloklaus/web-banner
Let users configure a message, colors, and scroll speed, preview it fullscreen, and share the setup via URL. The app can be installed on Android as a PWA.
- Edit mode: message (up to 100 characters), background color, text color, speed (x1, x2, x4)
- Play mode: fullscreen text, right-to-left scrolling, tap to pause/resume
- Sharing: Share button in edit and play modes (WhatsApp on mobile, copy link on desktop)
- Shareable URL: settings as readable query parameters (
m,b,f,s,p); defaults omitted - PWA: installable on Android via Chrome
- Vue 3 (
<script setup lang="ts">) - TypeScript
- Vite 6
- vite-plugin-pwa
- Cloudflare Pages
- Docker (for
make devandmake build)
Alternatively, Node.js 24+ and npm if you prefer to run without Docker.
Clone the repository and, from the project root:
git clone https://github.com/pauloklaus/web-banner.git
cd web-banner
make devThis starts an ephemeral Node 24 container, installs dependencies, and runs Vite at http://localhost:5173.
Press Ctrl+C in the terminal to stop.
Copy the example file and set your repository URL:
cp .env.example .env| Variable | Description |
|---|---|
VITE_APP_NAME |
App name shown in the UI, document title, and PWA manifest |
VITE_GITHUB_REPO_URL |
Repository URL linked from the footer |
VITE_SITE_URL |
Canonical site URL (Open Graph and meta tags) |
Restart the dev server after changing .env.
npm install
npm run devmake buildOutput is written to dist/.
make typecheckRuns vue-tsc --noEmit (also included in make build and npm run build).
make testRuns Vitest once (vitest run). For watch mode without Docker:
npm run test:watchCoverage report (terminal + coverage/ HTML). Fails if global coverage drops below 80% (lines, branches, functions, statements):
make coverageTests live next to the modules they cover (src/utils/*.test.ts).
The project is deployed to Cloudflare Pages at banner.pauloklaus.com.br.
| Setting | Value |
|---|---|
| Build command | npm run build |
| Build output directory | dist |
| Node.js version | 24 (see .node-version) |
SPA routing is handled by public/_redirects (/* → /index.html).
Connect the GitHub repository in the Cloudflare Pages dashboard and use the settings above. Builds and deploys run on Cloudflare — no GitHub Actions required.
PNG icons are generated from public/icons/icon.svg:
make iconsOr without Docker:
npm run generate:iconsThis creates icon-192.png, icon-512.png, apple-touch-icon.png, favicon-light.svg, favicon-dark.svg, matching PNG fallbacks, and localized Open Graph images (og-image-en.png, og-image-pt.png, og-image-es.png).
Favicons use separate light/dark assets selected via <link media="(prefers-color-scheme: ...)">: dark badge on light tabs, white rounded badge on dark tabs.
Supported languages: English (fallback), Portuguese (Brazil), and Spanish.
The UI language is detected from the browser (navigator.languages). Meta tags and the Open Graph image are updated at runtime to match the active locale.
Note: Social crawlers (e.g. WhatsApp link preview) read static HTML and typically show the English Open Graph defaults. In-app sharing uses the live URL; preview language may still be English unless server-side locale detection is added later.
Settings are stored as query parameters. Default values are omitted.
https://banner.pauloklaus.com.br/?m=Hello&b=%23f00&f=white&s=2&p=1
| Param | Meaning | Default |
|---|---|---|
m |
Message (max. 100 characters) | (empty) |
b |
Background color | #000000 (black) |
f |
Text (foreground) color | #ffffff (white) |
s |
Scroll speed (1, 2, or 4) |
1 |
p |
Auto-start in play mode (1 = yes) |
off |
Colors accept hex (#f00, #ff0000) or CSS names (white, red). Invalid values fall back to defaults.
- Open the app in Chrome (HTTPS or localhost)
- Tap Install (when available) or use Chrome menu → Install app
src/
├── App.vue # Global state and mode switching
├── main.ts # App bootstrap
├── components/ # Vue SFCs + barrel (index.ts)
├── composables/ # Reusable logic (URL, scroll, PWA, meta)
├── config/ # Env reader (readEnv.ts)
├── constants/ # Shared constants (defaults, limits, i18n locale)
├── i18n/ # vue-i18n setup and locales
├── types/ # Shared TypeScript types
├── test/ # Test helpers (e.g. createAppState)
└── utils/ # Pure helpers (text, URL config, colors)
See doc/CODE_GUIDE.md for coding conventions.
Contributions are welcome! Read doc/CODE_GUIDE.md before submitting changes.
- Language: English for documentation, commit messages, issues, and pull requests.
- Commits: Conventional Commits (
feat:,fix:,docs:, …) — details in the code guide.
Open an issue to report bugs or suggest improvements, or submit a pull request.
This project is open source under the MIT license.