mirror of
https://github.com/PR0M3TH3AN/TORCH.git
synced 2026-10-27 07:51:52 +00:00
Run ui-ux-agent: Improve landing page a11y and add style guide
- Created `docs/style-guide.md` documenting design system. - Refined `landing/index.html` with ARIA attributes and keyboard navigation for agent selection. - Verified changes with Playwright script. Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
1 parent
289bad78a8
commit
de8b62ef02
7 files changed
+233
-10
No files matched your search
@@ -0,0 +1,95 @@
|
||||
# TORCH Style Guide
|
||||
|
||||
This document outlines the design system, color palette, and typography used in the TORCH project (`dashboard/` and `landing/`).
|
||||
|
||||
## Color Palette
|
||||
|
||||
The project uses a dark theme with high-contrast text and vibrant accents.
|
||||
|
||||
### Core Colors
|
||||
|
||||
| Name | CSS Variable | Hex | Usage |
|
||||
|:---------------|:-------------------|:----------|:-------------------------------------------|
|
||||
| **Surface** | `--surface` | `#0b1020` | Main background color. |
|
||||
| **Surface Alt**| `--surface-alt` | `#151b2f` | Card backgrounds, headers, secondary areas.|
|
||||
| **Text** | `--text` | `#dbe4ff` | Primary body text (slightly blue-tinted). |
|
||||
| **Text Strong**| `--text-strong` | `#ffffff` | Headings, emphasized text. |
|
||||
| **Muted** | `--muted` | `#9aa6c6` | Secondary text, metadata, descriptions. |
|
||||
| **Border** | `--border` | `#2a3456` | Borders for cards, inputs, dividers. |
|
||||
|
||||
### Brand & Functional Colors
|
||||
|
||||
| Name | CSS Variable | Hex | Usage |
|
||||
|:---------------|:-------------------|:----------|:-------------------------------------------|
|
||||
| **Accent** | `--accent` | `#4f8cff` | Primary buttons, active states, links. |
|
||||
| **Accent Strong**| `--accent-strong`| `#2f6be5` | Hover states for accent elements. |
|
||||
| **Info** | `--info` | `#46b7ff` | Informational messages, icons. |
|
||||
| **Info Strong**| `--info-strong` | `#8ad4ff` | Stronger info emphasis. |
|
||||
| **Success** | `--success` | `#35c47c` | Success messages, "Active" statuses. |
|
||||
| **Warning** | `--warning` | `#f5b84b` | Warning alerts. |
|
||||
| **Danger** | `--danger` | `#ff6b6b` | Error messages, destructive actions. |
|
||||
|
||||
### Auxiliary Colors
|
||||
|
||||
| Name | CSS Variable | Hex |
|
||||
|:---------------|:-------------------|:----------|
|
||||
| **Purple** | `--purple` | `#a855f7` |
|
||||
| **Pink** | `--pink` | `#ec4899` |
|
||||
| **Teal** | `--teal` | `#2dd4bf` |
|
||||
|
||||
## Typography
|
||||
|
||||
### Font Families
|
||||
|
||||
* **Primary**: `Inter`, `system-ui`, `-apple-system`, `Segoe UI`, `Roboto`, `sans-serif`
|
||||
* **Monospace**: `ui-monospace`, `SFMono-Regular`, `Menlo`, `Monaco`, `Consolas`, `monospace`
|
||||
|
||||
### Hierarchy
|
||||
|
||||
* **Headings**: Bold (`700`), `Text Strong`.
|
||||
* **Body**: Regular (`400`), `Text`.
|
||||
* **Small/Meta**: Small size, `Muted`.
|
||||
|
||||
## Components
|
||||
|
||||
### Buttons
|
||||
|
||||
* **Primary CTA**:
|
||||
* Background: `var(--accent)`
|
||||
* Text: `white`
|
||||
* Border Radius: `9999px` (Pill shape)
|
||||
* Hover: `var(--accent-strong)`, slight transform up.
|
||||
* **Secondary CTA**:
|
||||
* Background: Transparent or low opacity white.
|
||||
* Border: `var(--border)`
|
||||
* Text: `var(--text)`
|
||||
* Hover: Lighter background, `Text Strong`.
|
||||
|
||||
### Cards
|
||||
|
||||
* Background: `var(--surface-alt)`
|
||||
* Border: `1px solid var(--border)`
|
||||
* Radius: `12px` or `1rem`
|
||||
* Hover: Border color changes to `var(--accent)`, slight lift (`transform: translateY(-5px)`).
|
||||
|
||||
### Status Badges
|
||||
|
||||
* Small, pill-shaped (`border-radius: 99px`).
|
||||
* Uppercase, bold, small font size.
|
||||
* Active: Background `var(--accent)`, Text `var(--surface)`.
|
||||
* Disabled/Inactive: Background `rgba(255,255,255,0.1)`, Text `var(--muted)`.
|
||||
|
||||
## Layout
|
||||
|
||||
* **Container**: Max-width `1200px` centered.
|
||||
* **Grid**: Responsive grids using `grid-template-columns: repeat(auto-fit, minmax(..., 1fr))`.
|
||||
* **Spacing**: Uses `rem` units (e.g., `1rem`, `2rem`, `4rem`).
|
||||
|
||||
## Accessibility Guidelines
|
||||
|
||||
* **Contrast**: Ensure text passes WCAG AA standards against the dark background.
|
||||
* **Interactive Elements**:
|
||||
* Must have `role="button"` or be `<button>`/`<a>`.
|
||||
* Must be keyboard accessible (`tabindex="0"`).
|
||||
* Must have visible focus states (`focus:ring`).
|
||||
* **Images**: All images must have `alt` text. Decorative images should use `aria-hidden="true"`.
|
||||
+24
-10
@@ -809,7 +809,7 @@
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="background-decoration">
|
||||
<div class="background-decoration" aria-hidden="true">
|
||||
<svg viewBox="0 0 1440 1024" fill="none" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice">
|
||||
<defs>
|
||||
<linearGradient id="warmGradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
@@ -874,8 +874,8 @@
|
||||
<div class="install-container scroll-reveal">
|
||||
<h2 class="mb-8 text-text-strong" style="font-size: 2rem;">Choose Your Agent</h2>
|
||||
|
||||
<div class="agent-selector">
|
||||
<div id="card-jules" onclick="selectAgent('jules')" class="agent-card active" style="cursor: pointer;">
|
||||
<div class="agent-selector" role="radiogroup" aria-label="Select an Agent">
|
||||
<div id="card-jules" onclick="selectAgent('jules')" class="agent-card active" style="cursor: pointer;" role="radio" aria-checked="true" tabindex="0">
|
||||
<div class="agent-header">
|
||||
<span class="agent-name">Jules</span>
|
||||
<span class="agent-status">Active</span>
|
||||
@@ -883,7 +883,7 @@
|
||||
<p class="agent-desc">Web-based tool with Auto-Scheduler. Optimized for Google's latest coding agent. <a href="https://jules.google/" target="_blank" onclick="event.stopPropagation()">Learn more</a>.</p>
|
||||
</div>
|
||||
|
||||
<div id="card-codex" onclick="selectAgent('codex')" class="agent-card" style="cursor: pointer;">
|
||||
<div id="card-codex" onclick="selectAgent('codex')" class="agent-card" style="cursor: pointer;" role="radio" aria-checked="false" tabindex="0">
|
||||
<div class="agent-header">
|
||||
<span class="agent-name">Codex CLI</span>
|
||||
<span class="agent-status">Active</span>
|
||||
@@ -891,7 +891,7 @@
|
||||
<p class="agent-desc">Advanced reasoning for complex tasks. (Requires full access permissions) <a href="https://openai.com/blog/openai-codex" target="_blank" onclick="event.stopPropagation()">Learn more</a>.</p>
|
||||
</div>
|
||||
|
||||
<div id="card-opencode" onclick="selectAgent('opencode')" class="agent-card" style="cursor: pointer;">
|
||||
<div id="card-opencode" onclick="selectAgent('opencode')" class="agent-card" style="cursor: pointer;" role="radio" aria-checked="false" tabindex="0">
|
||||
<div class="agent-header">
|
||||
<span class="agent-name">OpenCode CLI</span>
|
||||
<span class="agent-status">Active</span>
|
||||
@@ -899,7 +899,7 @@
|
||||
<p class="agent-desc">Open source models running locally. <a href="https://opencode.ai/" target="_blank" onclick="event.stopPropagation()">Learn more</a>.</p>
|
||||
</div>
|
||||
|
||||
<div id="card-claude" onclick="selectAgent('claude')" class="agent-card" style="cursor: pointer;">
|
||||
<div id="card-claude" onclick="selectAgent('claude')" class="agent-card" style="cursor: pointer;" role="radio" aria-checked="false" tabindex="0">
|
||||
<div class="agent-header">
|
||||
<span class="agent-name">Claude Code CLI</span>
|
||||
<span class="agent-status">Active</span>
|
||||
@@ -907,7 +907,7 @@
|
||||
<p class="agent-desc">Long-context understanding and safety. (CLI Required, needs cronjob) <a href="https://claude.ai/" target="_blank" onclick="event.stopPropagation()">Learn more</a>.</p>
|
||||
</div>
|
||||
|
||||
<div id="card-gemini" onclick="selectAgent('gemini')" class="agent-card" style="cursor: pointer;">
|
||||
<div id="card-gemini" onclick="selectAgent('gemini')" class="agent-card" style="cursor: pointer;" role="radio" aria-checked="false" tabindex="0">
|
||||
<div class="agent-header">
|
||||
<span class="agent-name">Gemini CLI</span>
|
||||
<span class="agent-status">Active</span>
|
||||
@@ -915,7 +915,7 @@
|
||||
<p class="agent-desc">Google's multimodal AI model. (CLI Required, needs cronjob) <a href="https://deepmind.google/technologies/gemini/" target="_blank" onclick="event.stopPropagation()">Learn more</a>.</p>
|
||||
</div>
|
||||
|
||||
<div id="card-qwen" onclick="selectAgent('qwen')" class="agent-card" style="cursor: pointer;">
|
||||
<div id="card-qwen" onclick="selectAgent('qwen')" class="agent-card" style="cursor: pointer;" role="radio" aria-checked="false" tabindex="0">
|
||||
<div class="agent-header">
|
||||
<span class="agent-name">Qwen Coder CLI</span>
|
||||
<span class="agent-status">Active</span>
|
||||
@@ -923,7 +923,7 @@
|
||||
<p class="agent-desc">Alibaba's open weights coding model. (CLI Required, needs cronjob) <a href="https://qwenlm.github.io/" target="_blank" onclick="event.stopPropagation()">Learn more</a>.</p>
|
||||
</div>
|
||||
|
||||
<div id="card-antigravity" onclick="selectAgent('antigravity')" class="agent-card" style="cursor: pointer;">
|
||||
<div id="card-antigravity" onclick="selectAgent('antigravity')" class="agent-card" style="cursor: pointer;" role="radio" aria-checked="false" tabindex="0">
|
||||
<div class="agent-header">
|
||||
<span class="agent-name">Antigravity IDE</span>
|
||||
<span class="agent-status">Active</span>
|
||||
@@ -1527,14 +1527,28 @@ npm install https://github.com/PR0M3TH3AN/TORCH/archive/refs/heads/main.tar.gz &
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
|
||||
// Handle keyboard navigation for agent cards
|
||||
document.querySelectorAll('.agent-card').forEach(card => {
|
||||
card.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
card.click();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function selectAgent(agent) {
|
||||
// Remove active class from all cards
|
||||
document.querySelectorAll('.agent-card').forEach(card => card.classList.remove('active'));
|
||||
document.querySelectorAll('.agent-card').forEach(card => {
|
||||
card.classList.remove('active');
|
||||
card.setAttribute('aria-checked', 'false');
|
||||
});
|
||||
|
||||
// Add active class to selected card
|
||||
const selectedCard = document.getElementById('card-' + agent);
|
||||
if (selectedCard) {
|
||||
selectedCard.classList.add('active');
|
||||
selectedCard.setAttribute('aria-checked', 'true');
|
||||
}
|
||||
|
||||
// Update prompt text
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
agent: ui-ux-agent
|
||||
cadence: weekly
|
||||
run-start: 2026-02-25T05:34:21Z
|
||||
status: completed
|
||||
---
|
||||
|
||||
## task_log
|
||||
|
||||
**Goal:** Improve UI/UX, documentation, and accessibility.
|
||||
|
||||
**Steps Taken:**
|
||||
1. Created `docs/style-guide.md` documenting the design system (colors, typography, components).
|
||||
2. Refined `landing/index.html` for accessibility:
|
||||
* Added `aria-hidden="true"` to decorative background.
|
||||
* Implemented keyboard navigation for agent cards (`tabindex="0"`, `keydown` handler).
|
||||
* Added ARIA roles (`radiogroup`, `radio`) and states (`aria-checked`).
|
||||
3. Verified changes using a Playwright script (`verify_landing.py`) to confirm keyboard navigation and state updates.
|
||||
4. Ran repository validation (`npm run lint`, `npm test`).
|
||||
|
||||
**Result:**
|
||||
The landing page is now more accessible and keyboard-friendly. A comprehensive style guide is available for future reference.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 115 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 114 KiB |
@@ -0,0 +1,43 @@
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
def run(playwright):
|
||||
browser = playwright.chromium.launch(headless=True)
|
||||
page = browser.new_page()
|
||||
page.goto("http://localhost:8080/landing/index.html")
|
||||
|
||||
# Wait for the page to load
|
||||
page.wait_for_selector(".agent-selector")
|
||||
|
||||
# 1. Verify default state (Jules selected)
|
||||
jules_card = page.locator("#card-jules")
|
||||
codex_card = page.locator("#card-codex")
|
||||
|
||||
print("Checking default state...")
|
||||
assert jules_card.get_attribute("aria-checked") == "true", "Jules card should be checked by default"
|
||||
assert codex_card.get_attribute("aria-checked") == "false", "Codex card should not be checked by default"
|
||||
|
||||
# 2. Test keyboard navigation and selection
|
||||
print("Testing keyboard interaction...")
|
||||
# Focus on the first card (Jules) - assume tab order is correct
|
||||
jules_card.focus()
|
||||
|
||||
# Tab to Codex
|
||||
page.keyboard.press("Tab")
|
||||
# Verify focus is on Codex (simulated by checking active element or just pressing Enter)
|
||||
# Actually, pressing Tab from Jules should go to Codex if tabindex="0" on both.
|
||||
|
||||
# Press Enter to select Codex
|
||||
page.keyboard.press("Enter")
|
||||
|
||||
# Verify Codex is selected
|
||||
assert codex_card.get_attribute("aria-checked") == "true", "Codex card should be checked after Enter"
|
||||
assert jules_card.get_attribute("aria-checked") == "false", "Jules card should be unchecked after selection change"
|
||||
|
||||
# 3. Take screenshot
|
||||
print("Taking screenshot...")
|
||||
page.screenshot(path="verification.png")
|
||||
|
||||
browser.close()
|
||||
|
||||
with sync_playwright() as playwright:
|
||||
run(playwright)
|
||||
@@ -0,0 +1,49 @@
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
def run(playwright):
|
||||
browser = playwright.chromium.launch(headless=True)
|
||||
page = browser.new_page()
|
||||
page.goto("http://localhost:8080/landing/index.html")
|
||||
|
||||
# Wait for the page to load
|
||||
page.wait_for_selector(".agent-selector")
|
||||
|
||||
# 1. Verify default state (Jules selected)
|
||||
jules_card = page.locator("#card-jules")
|
||||
codex_card = page.locator("#card-codex")
|
||||
|
||||
print("Checking default state...")
|
||||
assert jules_card.get_attribute("aria-checked") == "true", "Jules card should be checked by default"
|
||||
assert codex_card.get_attribute("aria-checked") == "false", "Codex card should not be checked by default"
|
||||
|
||||
# 2. Test keyboard navigation and selection
|
||||
print("Testing keyboard interaction...")
|
||||
# Focus on the first card (Jules)
|
||||
jules_card.focus()
|
||||
|
||||
# Tab to link inside Jules card
|
||||
page.keyboard.press("Tab")
|
||||
|
||||
# Tab to Codex card
|
||||
page.keyboard.press("Tab")
|
||||
|
||||
# Check if focused element is Codex card
|
||||
focused_id = page.evaluate("document.activeElement.id")
|
||||
assert focused_id == "card-codex", f"Expected focus on card-codex, but got {focused_id}"
|
||||
|
||||
# Press Enter to select Codex
|
||||
page.keyboard.press("Enter")
|
||||
|
||||
# Verify Codex is selected
|
||||
assert codex_card.get_attribute("aria-checked") == "true", "Codex card should be checked after Enter"
|
||||
assert jules_card.get_attribute("aria-checked") == "false", "Jules card should be unchecked after selection change"
|
||||
|
||||
# 3. Take screenshot
|
||||
print("Taking screenshot...")
|
||||
page.screenshot(path="verification.png")
|
||||
|
||||
browser.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
with sync_playwright() as playwright:
|
||||
run(playwright)
|
||||
Reference in new issue
Block a user