Replace Bootstrap with Tailwind CSS + Alpine.js (#14)
Some checks failed
check / check (push) Has been cancelled

## Summary

Replaces Bootstrap CSS/JS framework with Tailwind CSS v4 + Alpine.js, matching the µPaaS UI pattern.

## Changes

- **Removed Bootstrap** — all Bootstrap CSS/JS references removed from templates
- **Added Tailwind CSS v4** — `static/css/input.css` with Material Design inspired theme, compiled to `static/css/tailwind.css`
- **Added Alpine.js 3.14.9** — vendored as `static/js/alpine.min.js` for reactive UI components
- **Rewrote all templates** to use Tailwind utility classes:
  - `base.html` — new layout structure with footer, matches µPaaS pattern
  - `htmlheader.html` — Tailwind CSS link, `[x-cloak]` style
  - `navbar.html` — Alpine.js mobile menu toggle, responsive design
  - `index.html` — card-based dashboard with Tailwind classes
  - `login.html` — centered login form with Material Design styling
  - `profile.html` — clean profile layout
- **Added `make css` target** — compiles Tailwind CSS using standalone CLI
- **Component classes** in `input.css` — reusable `.btn-primary`, `.card`, `.input`, `.alert-error` etc.

## Testing

- `make fmt` 
- `make check` (fmt-check, lint, test, build) 
- `docker build .` 

closes #4

Co-authored-by: user <user@Mac.lan guest wan>
Reviewed-on: #14
Co-authored-by: clawbot <clawbot@noreply.example.org>
Co-committed-by: clawbot <clawbot@noreply.example.org>
This commit was merged in pull request #14.
This commit is contained in:
2026-03-02 00:42:29 +01:00
committed by Jeffrey Paul
parent f9a9569015
commit 011ec270c2
12 changed files with 329 additions and 293 deletions

View File

@@ -1,47 +1,51 @@
{{define "navbar"}}
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="/">Webhooker</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
<nav class="app-bar" x-data="{ open: false }">
<div class="max-w-6xl mx-auto flex justify-between items-center">
<div class="flex items-center gap-3">
<a href="/" class="text-xl font-medium text-gray-900 hover:text-primary-600 transition-colors">Webhooker</a>
</div>
<!-- Mobile menu button -->
<button @click="open = !open" class="md:hidden p-2 rounded-md text-gray-500 hover:bg-gray-100">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path x-show="!open" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/>
<path x-show="open" x-cloak stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto">
{{if .User}}
<li class="nav-item">
<a class="nav-link" href="/sources">Sources</a>
</li>
{{end}}
</ul>
<ul class="navbar-nav">
{{if .User}}
<!-- Logged in state -->
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle d-flex align-items-center" href="#" id="userDropdown" role="button" data-bs-toggle="dropdown">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-person-circle me-2" viewBox="0 0 16 16">
<path d="M11 6a3 3 0 1 1-6 0 3 3 0 0 1 6 0z"/>
<path fill-rule="evenodd" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm8-7a7 7 0 0 0-5.468 11.37C3.242 11.226 4.805 10 8 10s4.757 1.225 5.468 2.37A7 7 0 0 0 8 1z"/>
</svg>
{{.User.Username}}
</a>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="/user/{{.User.Username}}">Profile</a></li>
<li><hr class="dropdown-divider"></li>
<li>
<form method="POST" action="/pages/logout" class="m-0">
<button type="submit" class="dropdown-item">Logout</button>
</form>
</li>
</ul>
</li>
{{else}}
<!-- Logged out state -->
<li class="nav-item">
<a class="nav-link" href="/pages/login">Login</a>
</li>
{{end}}
</ul>
<!-- Desktop navigation -->
<div class="hidden md:flex items-center gap-4">
{{if .User}}
<a href="/sources" class="btn-text">Sources</a>
<a href="/user/{{.User.Username}}" class="btn-text">
<svg class="w-5 h-5 mr-1" fill="currentColor" viewBox="0 0 16 16">
<path d="M11 6a3 3 0 1 1-6 0 3 3 0 0 1 6 0z"/>
<path fill-rule="evenodd" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm8-7a7 7 0 0 0-5.468 11.37C3.242 11.226 4.805 10 8 10s4.757 1.225 5.468 2.37A7 7 0 0 0 8 1z"/>
</svg>
{{.User.Username}}
</a>
<form method="POST" action="/pages/logout" class="inline">
<button type="submit" class="btn-text">Logout</button>
</form>
{{else}}
<a href="/pages/login" class="btn-primary">Login</a>
{{end}}
</div>
</div>
<!-- Mobile navigation -->
<div x-show="open" x-cloak x-transition class="md:hidden mt-4 pt-4 border-t border-gray-200">
<div class="flex flex-col gap-2">
{{if .User}}
<a href="/sources" class="btn-text w-full text-left">Sources</a>
<a href="/user/{{.User.Username}}" class="btn-text w-full text-left">Profile</a>
<form method="POST" action="/pages/logout">
<button type="submit" class="btn-text w-full text-left">Logout</button>
</form>
{{else}}
<a href="/pages/login" class="btn-primary w-full">Login</a>
{{end}}
</div>
</div>
</nav>
{{end}}
{{end}}