UI Patterns

This document outlines the User Interface (UI) patterns used in the dracory/auth codebase.

Overview

The UI is built server-side using Go, leveraging the github.com/dracory/hb library to construct HTML programmatically. It relies heavily on Bootstrap 5 for styling and layout, with jQuery used for client-side interactivity.

Every login method ships a default page — page_login (OTP/password), page_magic_link (magic-link email form) — and each types.Method adapter accepts a custom page override when an application wants its own template.

Technology Stack

Core Patterns

1. Page Structure

Pages are constructed using a "Shell" pattern defined in internal/ui/shared/webpage.go.

2. Layout

3. Components

4. Client-Side Logic

Email Templates


Design Improvements

AuthKnight Register Page

The AuthKnight register page (/auth/register?verified_key=xxx) uses a distinct pattern for the email field:

AuthKnight Redirect Guard

All page handlers that are irrelevant in AuthKnight mode (login, register without verified_key, password-restore, password-reset, login-code-verify, register-code-verify) use a shared helper shared.RedirectAuthKnightIfActive() to redirect to AuthKnight's hosted login. This eliminates duplicated guard blocks across handlers.

The following improvements are recommended to enhance consistency, maintainability, and user experience.

1. Unify Typography

Current State: layout.go imports "Nunito", while shared/webpage.go sets font-family: Ubuntu, sans-serif on html,body and then overrides body with Nunito. Proposal:

2. Extract Reusable Components

Current State: The "Card" structure (Header, Body, Footer) is repeated in LoginContent, RegisterContent, etc. Proposal:

3. Externalize JavaScript

Current State: JavaScript is embedded as string literals in Go functions. This makes it hard to lint, format, and test. Proposal:

4. Centralize Styles

Current State: Styles are scattered across layout.go (inline CSS), shared/webpage.go (injected CSS string), and hb calls (.Style(...)). Proposal:

5. Enhance Email Templates

Current State: Basic HTML structure. Proposal:

Technical Improvements

1. Expand Public Interfaces

Current State: The authImplementation struct acts as a "shallow proxy" for UI pages. Methods like pageLogin manually construct a Dependencies struct, copying fields from authImplementation one by one. This is verbose, brittle, and violates the "Open/Closed" principle.

Problematic Code Example (auth_implementation_pages.go):

func (a authImplementation) pageLogin(w http.ResponseWriter, r *http.Request) {
    page_login.PageLogin(w, r, page_login.Dependencies{
        Otp:       a.passwordless,
        EnableRegistration: a.enableRegistration,
        Endpoint:           a.endpoint,
        RedirectOnSuccess:  a.LinkRedirectOnSuccess(),
        Layout:             a.funcLayout,
        Logger:             a.GetLogger(),
    })
}

Proposal: Expand the existing AuthSharedInterface (and its specialized counterparts) to include accessors for common dependencies. This allows the authImplementation (which already implements these interfaces) to be passed directly to UI handlers.

Recommended Approach: Expand Public Interfaces

  1. Expand Interface: Add accessor methods to types.AuthSharedInterface.

```go // types/auth_interfaces.go type AuthSharedInterface interface { // ... existing methods ...

// New Accessors GetEndpoint() string GetLogger() *slog.Logger IsRegistrationEnabled() bool GetLayout() func(w http.ResponseWriter, r *http.Request, title, body string, options LayoutOptions) string } ```

  1. Update Implementation: Ensure authImplementation satisfies these new methods. (Most are already present or trivial to add).
  1. Update Handlers: Update page handlers to accept the interface.

``go // internal/ui/page_login/page_login.go func PageLogin(w http.ResponseWriter, r *http.Request, auth types.AuthSharedInterface) { // Use auth.GetEndpoint(), auth.IsRegistrationEnabled(), etc. } ``

Benefits: