Nanny7 commited on
Commit
6d4f40e
·
1 Parent(s): 45dca16

Add UI/UX rules documentation endpoint at /ui-rules

Browse files

- Create comprehensive HTML documentation page following the rules themselves
- Accessible design with proper headings, focus states, and keyboard navigation
- Responsive layout with mobile support
- Dark mode support via prefers-color-scheme
- Respects prefers-reduced-motion
- Color-coded MUST/SHOULD/NEVER rules for easy scanning
- Link to UI rules from root endpoint

Files changed (1) hide show
  1. app.py +344 -2
app.py CHANGED
@@ -1,6 +1,6 @@
1
  from fastapi import FastAPI, HTTPException, status, UploadFile, File, Path as FastAPIPath
2
  from fastapi.middleware.cors import CORSMiddleware
3
- from fastapi.responses import JSONResponse, Response
4
  import logging
5
  from contextlib import asynccontextmanager
6
  from typing import List
@@ -136,7 +136,8 @@ def root():
136
  "execute_in_session": "/sessions/{session_id}/execute" if docker_available else "unavailable",
137
  "languages": "/languages",
138
  "health": "/health",
139
- "docs": "/docs"
 
140
  },
141
  "notice": "Docker is not available. Full functionality requires Docker. See /docs for API documentation." if not docker_available else None
142
  }
@@ -185,6 +186,347 @@ def list_languages():
185
  }
186
 
187
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  # ========== Stateless Execution (backward compatible) ==========
189
 
190
  @app.post("/execute", response_model=ExecutionResponse)
 
1
  from fastapi import FastAPI, HTTPException, status, UploadFile, File, Path as FastAPIPath
2
  from fastapi.middleware.cors import CORSMiddleware
3
+ from fastapi.responses import JSONResponse, Response, HTMLResponse
4
  import logging
5
  from contextlib import asynccontextmanager
6
  from typing import List
 
136
  "execute_in_session": "/sessions/{session_id}/execute" if docker_available else "unavailable",
137
  "languages": "/languages",
138
  "health": "/health",
139
+ "docs": "/docs",
140
+ "ui-rules": "/ui-rules"
141
  },
142
  "notice": "Docker is not available. Full functionality requires Docker. See /docs for API documentation." if not docker_available else None
143
  }
 
186
  }
187
 
188
 
189
+ @app.get("/ui-rules", response_class=HTMLResponse)
190
+ def ui_rules_docs():
191
+ """UI/UX Design Rules Documentation"""
192
+ html_content = """<!DOCTYPE html>
193
+ <html lang="en">
194
+ <head>
195
+ <meta charset="UTF-8">
196
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0">
197
+ <title>UI/UX Rules - isolated-sandbox</title>
198
+ <style>
199
+ * {
200
+ margin: 0;
201
+ padding: 0;
202
+ box-sizing: border-box;
203
+ }
204
+
205
+ :root {
206
+ --bg: #ffffff;
207
+ --text: #1a1a1a;
208
+ --accent: #0066cc;
209
+ --border: #e0e0e0;
210
+ --code-bg: #f5f5f5;
211
+ --must: #c53030;
212
+ --should: #d69e2e;
213
+ --never: #742a2a;
214
+ }
215
+
216
+ @media (prefers-color-scheme: dark) {
217
+ :root {
218
+ --bg: #1a1a1a;
219
+ --text: #e0e0e0;
220
+ --accent: #4a9eff;
221
+ --border: #333;
222
+ --code-bg: #2a2a2a;
223
+ --must: #ff6b6b;
224
+ --should: #ffd93d;
225
+ --never: #ff8787;
226
+ }
227
+ }
228
+
229
+ body {
230
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
231
+ line-height: 1.6;
232
+ color: var(--text);
233
+ background: var(--bg);
234
+ padding: 2rem;
235
+ max-width: 1000px;
236
+ margin: 0 auto;
237
+ }
238
+
239
+ h1 {
240
+ font-size: 2.5rem;
241
+ margin-bottom: 0.5rem;
242
+ scroll-margin-top: 2rem;
243
+ }
244
+
245
+ h2 {
246
+ font-size: 1.75rem;
247
+ margin-top: 3rem;
248
+ margin-bottom: 1rem;
249
+ padding-bottom: 0.5rem;
250
+ border-bottom: 2px solid var(--border);
251
+ scroll-margin-top: 2rem;
252
+ }
253
+
254
+ h3 {
255
+ font-size: 1.25rem;
256
+ margin-top: 2rem;
257
+ margin-bottom: 0.75rem;
258
+ scroll-margin-top: 2rem;
259
+ }
260
+
261
+ p {
262
+ margin-bottom: 1rem;
263
+ }
264
+
265
+ ul, ol {
266
+ margin-left: 1.5rem;
267
+ margin-bottom: 1rem;
268
+ }
269
+
270
+ li {
271
+ margin-bottom: 0.5rem;
272
+ }
273
+
274
+ code {
275
+ background: var(--code-bg);
276
+ padding: 0.2em 0.4em;
277
+ border-radius: 3px;
278
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
279
+ font-size: 0.9em;
280
+ }
281
+
282
+ pre {
283
+ background: var(--code-bg);
284
+ padding: 1rem;
285
+ border-radius: 6px;
286
+ overflow-x: auto;
287
+ margin: 1rem 0;
288
+ }
289
+
290
+ pre code {
291
+ background: none;
292
+ padding: 0;
293
+ }
294
+
295
+ .must {
296
+ color: var(--must);
297
+ font-weight: 600;
298
+ }
299
+
300
+ .should {
301
+ color: var(--should);
302
+ font-weight: 600;
303
+ }
304
+
305
+ .never {
306
+ color: var(--never);
307
+ font-weight: 600;
308
+ }
309
+
310
+ .rule-item {
311
+ margin-bottom: 1rem;
312
+ padding-left: 1rem;
313
+ border-left: 3px solid var(--border);
314
+ }
315
+
316
+ .rule-item .must {
317
+ border-left-color: var(--must);
318
+ }
319
+
320
+ .rule-item .should {
321
+ border-left-color: var(--should);
322
+ }
323
+
324
+ .rule-item .never {
325
+ border-left-color: var(--never);
326
+ }
327
+
328
+ a {
329
+ color: var(--accent);
330
+ text-decoration: none;
331
+ }
332
+
333
+ a:hover, a:focus {
334
+ text-decoration: underline;
335
+ outline: 2px solid var(--accent);
336
+ outline-offset: 2px;
337
+ }
338
+
339
+ a:focus-visible {
340
+ outline: 2px solid var(--accent);
341
+ outline-offset: 2px;
342
+ }
343
+
344
+ nav {
345
+ margin-bottom: 2rem;
346
+ padding-bottom: 1rem;
347
+ border-bottom: 1px solid var(--border);
348
+ }
349
+
350
+ nav a {
351
+ margin-right: 1rem;
352
+ }
353
+
354
+ @media (max-width: 768px) {
355
+ body {
356
+ padding: 1rem;
357
+ }
358
+
359
+ h1 {
360
+ font-size: 2rem;
361
+ }
362
+
363
+ h2 {
364
+ font-size: 1.5rem;
365
+ }
366
+ }
367
+
368
+ @media (prefers-reduced-motion: reduce) {
369
+ * {
370
+ animation-duration: 0.01ms !important;
371
+ animation-iteration-count: 1 !important;
372
+ transition-duration: 0.01ms !important;
373
+ }
374
+ }
375
+ </style>
376
+ </head>
377
+ <body>
378
+ <nav>
379
+ <a href="/">Home</a>
380
+ <a href="/docs">API Docs</a>
381
+ <a href="/ui-rules">UI Rules</a>
382
+ </nav>
383
+
384
+ <main>
385
+ <h1>UI/UX Design Rules</h1>
386
+ <p>Concise rules for building accessible, fast, delightful UIs. Use <span class="must">MUST</span>/<span class="should">SHOULD</span>/<span class="never">NEVER</span> to guide decisions.</p>
387
+
388
+ <h2>Interactions</h2>
389
+
390
+ <h3>Keyboard</h3>
391
+ <ul>
392
+ <li class="rule-item"><span class="must">MUST:</span> Full keyboard support per <a href="https://www.w3.org/WAI/ARIA/apg/patterns/" target="_blank" rel="noopener">WAI-ARIA APG</a></li>
393
+ <li class="rule-item"><span class="must">MUST:</span> Visible focus rings (<code>:focus-visible</code>; group with <code>:focus-within</code>)</li>
394
+ <li class="rule-item"><span class="must">MUST:</span> Manage focus (trap, move, and return) per APG patterns</li>
395
+ </ul>
396
+
397
+ <h3>Targets &amp; Input</h3>
398
+ <ul>
399
+ <li class="rule-item"><span class="must">MUST:</span> Hit target ≥24px (mobile ≥44px) If visual &lt;24px, expand hit area</li>
400
+ <li class="rule-item"><span class="must">MUST:</span> Mobile <code>&lt;input&gt;</code> font-size ≥16px or set:
401
+ <pre><code>&lt;meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover"&gt;</code></pre>
402
+ </li>
403
+ <li class="rule-item"><span class="never">NEVER:</span> Disable browser zoom</li>
404
+ <li class="rule-item"><span class="must">MUST:</span> <code>touch-action: manipulation</code> to prevent double-tap zoom; set <code>-webkit-tap-highlight-color</code> to match design</li>
405
+ </ul>
406
+
407
+ <h3>Inputs &amp; Forms (Behavior)</h3>
408
+ <ul>
409
+ <li class="rule-item"><span class="must">MUST:</span> Hydration-safe inputs (no lost focus/value)</li>
410
+ <li class="rule-item"><span class="never">NEVER:</span> Block paste in <code>&lt;input&gt;/&lt;textarea&gt;</code></li>
411
+ <li class="rule-item"><span class="must">MUST:</span> Loading buttons show spinner and keep original label</li>
412
+ <li class="rule-item"><span class="must">MUST:</span> Enter submits focused text input In <code>&lt;textarea&gt;</code>, ⌘/Ctrl+Enter submits; Enter adds newline</li>
413
+ <li class="rule-item"><span class="must">MUST:</span> Keep submit enabled until request starts; then disable, show spinner, use idempotency key</li>
414
+ <li class="rule-item"><span class="must">MUST:</span> Don't block typing; accept free text and validate after</li>
415
+ <li class="rule-item"><span class="must">MUST:</span> Allow submitting incomplete forms to surface validation</li>
416
+ <li class="rule-item"><span class="must">MUST:</span> Errors inline next to fields; on submit, focus first error</li>
417
+ <li class="rule-item"><span class="must">MUST:</span> <code>autocomplete</code> + meaningful <code>name</code>; correct <code>type</code> and <code>inputmode</code></li>
418
+ <li class="rule-item"><span class="should">SHOULD:</span> Disable spellcheck for emails/codes/usernames</li>
419
+ <li class="rule-item"><span class="should">SHOULD:</span> Placeholders end with ellipsis and show example pattern (eg, <code>+1 (123) 456-7890</code>, <code>sk-012345…</code>)</li>
420
+ <li class="rule-item"><span class="must">MUST:</span> Warn on unsaved changes before navigation</li>
421
+ <li class="rule-item"><span class="must">MUST:</span> Compatible with password managers &amp; 2FA; allow pasting one-time codes</li>
422
+ <li class="rule-item"><span class="must">MUST:</span> Trim values to handle text expansion trailing spaces</li>
423
+ <li class="rule-item"><span class="must">MUST:</span> No dead zones on checkboxes/radios; label+control share one generous hit target</li>
424
+ </ul>
425
+
426
+ <h3>State &amp; Navigation</h3>
427
+ <ul>
428
+ <li class="rule-item"><span class="must">MUST:</span> URL reflects state (deep-link filters/tabs/pagination/expanded panels) Prefer libs like <a href="https://nuqs.dev" target="_blank" rel="noopener">nuqs</a></li>
429
+ <li class="rule-item"><span class="must">MUST:</span> Back/Forward restores scroll</li>
430
+ <li class="rule-item"><span class="must">MUST:</span> Links are links—use <code>&lt;a&gt;/&lt;Link&gt;</code> for navigation (support Cmd/Ctrl/middle-click)</li>
431
+ </ul>
432
+
433
+ <h3>Feedback</h3>
434
+ <ul>
435
+ <li class="rule-item"><span class="should">SHOULD:</span> Optimistic UI; reconcile on response; on failure show error and rollback or offer Undo</li>
436
+ <li class="rule-item"><span class="must">MUST:</span> Confirm destructive actions or provide Undo window</li>
437
+ <li class="rule-item"><span class="must">MUST:</span> Use polite <code>aria-live</code> for toasts/inline validation</li>
438
+ <li class="rule-item"><span class="should">SHOULD:</span> Ellipsis (<code>…</code>) for options that open follow-ups (eg, "Rename…") and loading states (eg, "Loading…", "Saving…", "Generating…")</li>
439
+ </ul>
440
+
441
+ <h3>Touch/Drag/Scroll</h3>
442
+ <ul>
443
+ <li class="rule-item"><span class="must">MUST:</span> Design forgiving interactions (generous targets, clear affordances; avoid finickiness)</li>
444
+ <li class="rule-item"><span class="must">MUST:</span> Delay first tooltip in a group; subsequent peers no delay</li>
445
+ <li class="rule-item"><span class="must">MUST:</span> Intentional <code>overscroll-behavior: contain</code> in modals/drawers</li>
446
+ <li class="rule-item"><span class="must">MUST:</span> During drag, disable text selection and set <code>inert</code> on dragged element/containers</li>
447
+ <li class="rule-item"><span class="must">MUST:</span> No "dead-looking" interactive zones—if it looks clickable, it is</li>
448
+ </ul>
449
+
450
+ <h3>Autofocus</h3>
451
+ <ul>
452
+ <li class="rule-item"><span class="should">SHOULD:</span> Autofocus on desktop when there's a single primary input; rarely on mobile (to avoid layout shift)</li>
453
+ </ul>
454
+
455
+ <h2>Animation</h2>
456
+ <ul>
457
+ <li class="rule-item"><span class="must">MUST:</span> Honor <code>prefers-reduced-motion</code> (provide reduced variant)</li>
458
+ <li class="rule-item"><span class="should">SHOULD:</span> Prefer CSS &gt; Web Animations API &gt; JS libraries</li>
459
+ <li class="rule-item"><span class="must">MUST:</span> Animate compositor-friendly props (<code>transform</code>, <code>opacity</code>); avoid layout/repaint props (<code>top/left/width/height</code>)</li>
460
+ <li class="rule-item"><span class="should">SHOULD:</span> Animate only to clarify cause/effect or add deliberate delight</li>
461
+ <li class="rule-item"><span class="should">SHOULD:</span> Choose easing to match the change (size/distance/trigger)</li>
462
+ <li class="rule-item"><span class="must">MUST:</span> Animations are interruptible and input-driven (avoid autoplay)</li>
463
+ <li class="rule-item"><span class="must">MUST:</span> Correct <code>transform-origin</code> (motion starts where it "physically" should)</li>
464
+ </ul>
465
+
466
+ <h2>Layout</h2>
467
+ <ul>
468
+ <li class="rule-item"><span class="should">SHOULD:</span> Optical alignment; adjust by ±1px when perception beats geometry</li>
469
+ <li class="rule-item"><span class="must">MUST:</span> Deliberate alignment to grid/baseline/edges/optical centers—no accidental placement</li>
470
+ <li class="rule-item"><span class="should">SHOULD:</span> Balance icon/text lockups (stroke/weight/size/spacing/color)</li>
471
+ <li class="rule-item"><span class="must">MUST:</span> Verify mobile, laptop, ultra-wide (simulate ultra-wide at 50% zoom)</li>
472
+ <li class="rule-item"><span class="must">MUST:</span> Respect safe areas (use <code>env(safe-area-inset-*)</code>)</li>
473
+ <li class="rule-item"><span class="must">MUST:</span> Avoid unwanted scrollbars; fix overflows</li>
474
+ </ul>
475
+
476
+ <h2>Content &amp; Accessibility</h2>
477
+ <ul>
478
+ <li class="rule-item"><span class="should">SHOULD:</span> Inline help first; tooltips last resort</li>
479
+ <li class="rule-item"><span class="must">MUST:</span> Skeletons mirror final content to avoid layout shift</li>
480
+ <li class="rule-item"><span class="must">MUST:</span> <code>&lt;title&gt;</code> matches current context</li>
481
+ <li class="rule-item"><span class="must">MUST:</span> No dead ends; always offer next step/recovery</li>
482
+ <li class="rule-item"><span class="must">MUST:</span> Design empty/sparse/dense/error states</li>
483
+ <li class="rule-item"><span class="should">SHOULD:</span> Curly quotes (" "); avoid widows/orphans</li>
484
+ <li class="rule-item"><span class="must">MUST:</span> Tabular numbers for comparisons (<code>font-variant-numeric: tabular-nums</code> or a mono like Geist Mono)</li>
485
+ <li class="rule-item"><span class="must">MUST:</span> Redundant status cues (not color-only); icons have text labels</li>
486
+ <li class="rule-item"><span class="must">MUST:</span> Don't ship the schema—visuals may omit labels but accessible names still exist</li>
487
+ <li class="rule-item"><span class="must">MUST:</span> Use the ellipsis character <code>…</code> (not <code>...</code>)</li>
488
+ <li class="rule-item"><span class="must">MUST:</span> <code>scroll-margin-top</code> on headings for anchored links; include a "Skip to content" link; hierarchical <code>&lt;h1–h6&gt;</code></li>
489
+ <li class="rule-item"><span class="must">MUST:</span> Resilient to user-generated content (short/avg/very long)</li>
490
+ <li class="rule-item"><span class="must">MUST:</span> Locale-aware dates/times/numbers/currency</li>
491
+ <li class="rule-item"><span class="must">MUST:</span> Accurate names (<code>aria-label</code>), decorative elements <code>aria-hidden</code>, verify in the Accessibility Tree</li>
492
+ <li class="rule-item"><span class="must">MUST:</span> Icon-only buttons have descriptive <code>aria-label</code></li>
493
+ <li class="rule-item"><span class="must">MUST:</span> Prefer native semantics (<code>button</code>, <code>a</code>, <code>label</code>, <code>table</code>) before ARIA</li>
494
+ <li class="rule-item"><span class="should">SHOULD:</span> Right-clicking the nav logo surfaces brand assets</li>
495
+ <li class="rule-item"><span class="must">MUST:</span> Use non-breaking spaces to glue terms: <code>10&nbsp;MB</code>, <code>⌘&nbsp;+&nbsp;K</code>, <code>Vercel&nbsp;SDK</code></li>
496
+ </ul>
497
+
498
+ <h2>Performance</h2>
499
+ <ul>
500
+ <li class="rule-item"><span class="should">SHOULD:</span> Test iOS Low Power Mode and macOS Safari</li>
501
+ <li class="rule-item"><span class="must">MUST:</span> Measure reliably (disable extensions that skew runtime)</li>
502
+ <li class="rule-item"><span class="must">MUST:</span> Track and minimize re-renders (React DevTools/React Scan)</li>
503
+ <li class="rule-item"><span class="must">MUST:</span> Profile with CPU/network throttling</li>
504
+ <li class="rule-item"><span class="must">MUST:</span> Batch layout reads/writes; avoid unnecessary reflows/repaints</li>
505
+ <li class="rule-item"><span class="must">MUST:</span> Mutations (<code>POST/PATCH/DELETE</code>) target &lt;500 ms</li>
506
+ <li class="rule-item"><span class="should">SHOULD:</span> Prefer uncontrolled inputs; make controlled loops cheap (keystroke cost)</li>
507
+ <li class="rule-item"><span class="must">MUST:</span> Virtualize large lists (eg, <code>virtua</code>)</li>
508
+ <li class="rule-item"><span class="must">MUST:</span> Preload only above-the-fold images; lazy-load the rest</li>
509
+ <li class="rule-item"><span class="must">MUST:</span> Prevent CLS from images (explicit dimensions or reserved space)</li>
510
+ </ul>
511
+
512
+ <h2>Design</h2>
513
+ <ul>
514
+ <li class="rule-item"><span class="should">SHOULD:</span> Layered shadows (ambient + direct)</li>
515
+ <li class="rule-item"><span class="should">SHOULD:</span> Crisp edges via semi-transparent borders + shadows</li>
516
+ <li class="rule-item"><span class="should">SHOULD:</span> Nested radii: child ≤ parent; concentric</li>
517
+ <li class="rule-item"><span class="should">SHOULD:</span> Hue consistency: tint borders/shadows/text toward bg hue</li>
518
+ <li class="rule-item"><span class="must">MUST:</span> Accessible charts (color-blind-friendly palettes)</li>
519
+ <li class="rule-item"><span class="must">MUST:</span> Meet contrast—prefer <a href="https://apcacontrast.com/" target="_blank" rel="noopener">APCA</a> over WCAG 2</li>
520
+ <li class="rule-item"><span class="must">MUST:</span> Increase contrast on <code>:hover/:active/:focus</code></li>
521
+ <li class="rule-item"><span class="should">SHOULD:</span> Match browser UI to bg</li>
522
+ <li class="rule-item"><span class="should">SHOULD:</span> Avoid gradient banding (use masks when needed)</li>
523
+ </ul>
524
+ </main>
525
+ </body>
526
+ </html>"""
527
+ return html_content
528
+
529
+
530
  # ========== Stateless Execution (backward compatible) ==========
531
 
532
  @app.post("/execute", response_model=ExecutionResponse)