Overall QA Progress
0%
Architecture Standards
These are Brad's established, non-negotiable standards for all apps built with Sterling. Every new project follows these unless explicitly overridden.
Data Layer
D1 for ALL User Data REQUIRED
Every piece of data a user creates, edits, or interacts with MUST be stored in a Cloudflare D1 database. This includes tasks, settings, preferences, checklists, categories, dropdown options, templates, board data... everything. The ONLY acceptable localStorage uses are auth/session tokens, ephemeral form drafts that auto-clear, and one-time migration flags.
Cross-Device Sync by Default REQUIRED
If Brad opens the app on his phone and then on his desktop, it must show the exact same data. This is automatic when using D1 correctly. Test this explicitly before launch.
D1 Table + API Endpoint FIRST REQUIRED
When building any new feature, the D1 table and API endpoint come first, before any frontend code. The frontend is just a view of the database state.
Hosting & Deployment
Cloudflare Pages + Functions REQUIRED
Standard architecture: static frontend in /public/, API endpoints in /functions/api/. D1 bindings in wrangler.toml. Deploy via wrangler pages deploy.
Brad's Personal Cloudflare Account (Default) REQUIRED
Account ID: f80e8ac3dae61d519e12427b58f8bd4d. ALL deployments go here unless Brad explicitly says "OA Cloudflare." Business-related or not, personal account is the default.
SSL Active REQUIRED
HTTPS with proper redirect. Cloudflare handles this automatically for .pages.dev domains. Custom domains need DNS configured through Cloudflare.
Design System
Sterling Pages Design System REQUIRED
Light gray background (#f5f5f7), white cards (#ffffff), purple gradient header (linear-gradient(160deg, #0f0c29 0%, #302b63 50%, #24243e 100%)), primary accent #7c3aed, Inter font. Tab navigation ALWAYS (never scroll-down sections).
Branded Mode Override OVERRIDE ONLY
Custom brand identity used ONLY for standalone product sites (DugoutReady, Automate & Delegate, client websites). Brad must explicitly direct this.
Refresh Button REQUIRED
Every page gets a 36x36px refresh button in the top-right header. White semi-transparent, SVG refresh icon, spin animation on click, then location.reload(true). Must preserve current tab via sessionStorage. Brad saves pages as web apps on his phone.
Light Theme Default RECOMMENDED
Brad explicitly chose light over dark for readability. White/light backgrounds with dark text.
Authentication
Clerk for Retail Products RECOMMENDED
Self-signup apps where unknown users create accounts. Clerk handles auth, password reset, session management, and edge cases (browser cookie changes, Apple security updates) automatically. Free for 10K users.
Custom HMAC JWT for Internal/Invite-Only RECOMMENDED
When Brad controls who gets access. Backend-created accounts with invite codes or admin-set credentials. Simpler, no third-party dependency.
API Auth Enforced REQUIRED
Every API endpoint must verify authentication. No unprotected write endpoints. Auth check happens at the API level, not just the frontend.
API Design
RESTful Endpoints in /functions/api/ REQUIRED
Standard REST conventions: GET for reads, POST for creates, PUT/PATCH for updates, DELETE for removes. Consistent JSON response format with error handling.
CORS Configuration REQUIRED
Set appropriate CORS headers. For same-origin apps on Pages, this is often automatic. For cross-origin consumers, configure explicitly.
Error Handling Pattern REQUIRED
All API responses return { success: true, data: ... } or { success: false, error: "message" }. HTTP status codes used correctly (200, 201, 400, 401, 404, 500).
UX Patterns
Auto-Save with Toast REQUIRED
Settings and configuration changes auto-save on change/blur. Show a brief toast notification ("Saved") confirming the save. No save buttons unless Brad specifically requests one.
Dropdown Management: Both REQUIRED
Users can create new dropdown options on-demand in the section where they're used (type a new value, it gets added). Edit and delete in Settings. Best of both worlds.
Mobile-Desktop Feature Parity REQUIRED
Zero tolerance. Every feature accessible on mobile must be accessible on desktop, and vice versa. Bottom nav "More" menu items must have desktop equivalents.
Touch Targets 44px+ REQUIRED
All interactive elements must be at least 44x44px on mobile for comfortable tapping. Apple's HIG standard.
Launch Checklist (Non-Negotiable)
Favicon REQUIRED
Custom favicon. Never the default platform icon. Verify visually in browser tab.
Site Icon 512x512 REQUIRED
For bookmark and home screen use on all devices.
Apple Touch Icon 180x180 REQUIRED
iOS home screen icon. Without this, iOS uses a screenshot which looks terrible.
OG Image 1200x630 REQUIRED
Social sharing preview image. When someone shares the URL, this image appears.
Mobile Responsive REQUIRED
Tested at phone (375px), tablet (768px), and desktop (1200px+) widths minimum.
Active Apps Portfolio
All apps Brad has built with Sterling, with architecture details and status.
Decision Framework
Visual decision trees for the most common architectural choices. Use these to make fast, consistent decisions on every new project.
Auth Strategy
Who creates user accounts?
What Goes in D1?
Does a user create, edit, or interact with this data?
Does Brad need to see the same data on phone AND desktop?
PWA vs Native App
Does the app need native device capabilities?
PWA Advantages
- Sterling builds in minutes, not weeks
- No app store approval process
- Instant updates (no user download)
- Works on iOS, Android, AND desktop
- Single codebase
- Installable to home screen
- Offline support with service worker
- No app store fees (15-30%)
- Chrome + Samsung Internet on Android handle PWAs great
PWA Limitations
- iOS Safari has some PWA restrictions
- No App Store/Play Store discoverability
- Limited background processing on iOS
- Push notifications limited on iOS (but improving)
- No access to some native APIs
- Can't sell through app stores
Build vs Buy (The Back Deck Test)
What type of component is this?
Sterling Build Time Benchmarks
| Project Type | Human Cost | Sterling Time |
|---|---|---|
| Full auth system (12 features) | $5-10K, 1-2 sprints | ~8 minutes |
| Client landing page | $2-4K, 3-5 days | 15-30 minutes |
| API integration | $1-3K, 2-4 days | 5-15 minutes |
| Dashboard / reporting tool | $5-15K, 2-4 weeks | 30-60 minutes |
| Team/roster page | $3-5K, 1-2 weeks | <1 hour |
Hosting Platform
What kind of site is this?
Reusable Components Library
Patterns Brad has across multiple apps. When building a new app, pull from these established patterns rather than inventing from scratch.
Tab Navigation with Reordering
Top tab bar with horizontal scroll on mobile. User can drag-reorder tabs. Active tab preserved in sessionStorage across refreshes. Tab order saved to D1 per user.
Key Implementation Notes
- sessionStorage.setItem('activeTab', tabId) on click
- On load: check sessionStorage for saved tab
- Tab order: D1 table `user_settings` with key='tab_order', value=JSON array
- Mobile: overflow-x: auto, -webkit-overflow-scrolling: touch
- Hide scrollbar: ::-webkit-scrollbar { display: none }
- Active indicator: border-bottom: 2px solid #7c3aed
Bottom Nav with "More" Menu
Fixed bottom navigation on mobile (max 4-5 items visible). Overflow items go into a "More" sheet that slides up. Desktop shows all items in top tab bar or sidebar. Feature parity is mandatory.
Key Implementation Notes
- position: fixed; bottom: 0; left: 0; right: 0
- Safe area: padding-bottom: env(safe-area-inset-bottom)
- "More" button opens overlay with remaining tabs
- Desktop: @media (min-width: 768px) { .bottom-nav { display: none } }
- CRITICAL: Every item in "More" must have a desktop equivalent
- Touch targets: min 44px height per item
Settings Panel with Auto-Save
Settings changes auto-save on blur/change. Toast notification confirms save. No save buttons. Dropdowns manageable in Settings (edit/delete) and createable on-demand in context.
Key Implementation Notes
- input.addEventListener('change', async () => {
await fetch('/api/settings', { method: 'PUT', body: ... });
showToast('Saved');
});
- Toast: fixed bottom center, fade in/out, 2s duration
- Category/dropdown management:
- In context: type new value, "Add" button, POST /api/categories
- In Settings: list all, edit inline, delete with confirm
- All settings stored in D1 `user_settings` table
D1-Backed CRUD Pattern
Standard pattern for any entity. API endpoints in /functions/api/[entity].js. D1 binding in wrangler.toml. Frontend fetches on load, optimistic UI on write.
Standard API Structure
// functions/api/items.js
export async function onRequestGet({ env }) {
const { results } = await env.DB.prepare(
'SELECT * FROM items ORDER BY created_at DESC'
).all();
return Response.json({ success: true, data: results });
}
export async function onRequestPost({ request, env }) {
const body = await request.json();
const result = await env.DB.prepare(
'INSERT INTO items (id, title, ...) VALUES (?, ?, ...)'
).bind(crypto.randomUUID(), body.title, ...).run();
return Response.json({ success: true }, { status: 201 });
}
// wrangler.toml
[[d1_databases]]
binding = "DB"
database_name = "my-app-db"
database_id = "uuid-here"
Auth Gate (PIN / Username-Password)
Simple auth gate for internal tools. PIN mode: single shared PIN. Username/password mode: per-user credentials stored in D1 with bcrypt hashing. JWT issued on success, stored in localStorage.
Key Implementation Notes
PIN Mode:
- Single PIN stored as env var or D1 setting
- On match: set localStorage token, show app
- No user accounts needed
Username/Password Mode:
- D1 `users` table: id, username, password_hash, role
- Login: POST /api/auth/login -> verify bcrypt -> issue JWT
- JWT: HMAC-SHA256, include user_id + role + exp
- Every API call: verify JWT in Authorization header
- Frontend: check localStorage for valid JWT on load
Clerk Mode (retail):
- npm install @clerk/clerk-js
- Wrap app in ClerkProvider
- useAuth() hook for state
- Middleware for API protection
Toast Notifications
Non-intrusive feedback for user actions. Fixed position, bottom center. Auto-dismiss after 2-3 seconds. Multiple types: success (green), error (red), info (blue).
Key Implementation Notes
function showToast(message, type = 'success') {
const t = document.getElementById('toast');
t.textContent = message;
t.className = 'toast show ' + type;
setTimeout(() => t.className = 'toast', 2500);
}
CSS:
.toast { position: fixed; bottom: 24px; left: 50%;
transform: translateX(-50%); padding: 10px 20px;
border-radius: 8px; font-size: 0.85rem;
opacity: 0; transition: opacity 0.3s; z-index: 1000; }
.toast.show { opacity: 1; }
.toast.success { background: #065f46; color: #fff; }
.toast.error { background: #991b1b; color: #fff; }
Pull-to-Refresh
Mobile gesture to refresh data. Shows spinner animation during refresh. Works alongside the header refresh button.
Key Implementation Notes
- Track touchstart Y position
- On touchmove: if scrollTop === 0 and pulling down > 60px
- Show refresh indicator
- On touchend: call data refresh function
- After fetch complete: hide indicator
- Alternative: use header refresh button (works on all platforms)
- iOS PWA: pull-to-refresh sometimes conflicts with Safari's
built-in gesture. Test carefully.
Service Worker with Cache Busting
Enables offline access and faster loads. Version-stamped cache names so updates deploy cleanly. Handles the "stale app" problem.
Key Implementation Notes
// sw.js
const CACHE_NAME = 'app-v1.0.0'; // Bump on deploy
const ASSETS = ['/', '/index.html', '/styles.css'];
self.addEventListener('install', e => {
e.waitUntil(caches.open(CACHE_NAME)
.then(c => c.addAll(ASSETS)));
self.skipWaiting();
});
self.addEventListener('activate', e => {
e.waitUntil(caches.keys().then(keys =>
Promise.all(keys.filter(k => k !== CACHE_NAME)
.map(k => caches.delete(k)))));
});
// Network-first for API, cache-first for assets
self.addEventListener('fetch', e => {
if (e.request.url.includes('/api/')) {
e.respondWith(fetch(e.request));
} else {
e.respondWith(caches.match(e.request)
.then(r => r || fetch(e.request)));
}
});
Category Management (On-Demand + Settings)
Users create categories/dropdown values wherever they need them (inline "Add new" in dropdowns). Full management (rename, reorder, delete) lives in Settings. Both backed by D1.
Key Implementation Notes
D1 Table:
CREATE TABLE categories (
id TEXT PRIMARY KEY,
type TEXT NOT NULL, -- 'vendor_type', 'task_category', etc.
label TEXT NOT NULL,
sort_order INTEGER DEFAULT 0,
created_at TEXT DEFAULT (datetime('now'))
);
In-context creation:
- Dropdown shows existing + "Add new..." option at bottom
- Selecting "Add new..." shows inline input
- On submit: POST /api/categories, refresh dropdown, select new
Settings management:
- List all categories grouped by type
- Inline edit (click to rename)
- Drag to reorder (update sort_order)
- Delete with confirmation ("This will remove the category.
Items using it will become uncategorized.")