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>
|
</head>
|
||||||
<body>
|
<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">
|
<svg viewBox="0 0 1440 1024" fill="none" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice">
|
||||||
<defs>
|
<defs>
|
||||||
<linearGradient id="warmGradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
<linearGradient id="warmGradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||||
@@ -874,8 +874,8 @@
|
|||||||
<div class="install-container scroll-reveal">
|
<div class="install-container scroll-reveal">
|
||||||
<h2 class="mb-8 text-text-strong" style="font-size: 2rem;">Choose Your Agent</h2>
|
<h2 class="mb-8 text-text-strong" style="font-size: 2rem;">Choose Your Agent</h2>
|
||||||
|
|
||||||
<div class="agent-selector">
|
<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;">
|
<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">
|
<div class="agent-header">
|
||||||
<span class="agent-name">Jules</span>
|
<span class="agent-name">Jules</span>
|
||||||
<span class="agent-status">Active</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>
|
<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>
|
||||||
|
|
||||||
<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">
|
<div class="agent-header">
|
||||||
<span class="agent-name">Codex CLI</span>
|
<span class="agent-name">Codex CLI</span>
|
||||||
<span class="agent-status">Active</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>
|
<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>
|
||||||
|
|
||||||
<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">
|
<div class="agent-header">
|
||||||
<span class="agent-name">OpenCode CLI</span>
|
<span class="agent-name">OpenCode CLI</span>
|
||||||
<span class="agent-status">Active</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>
|
<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>
|
||||||
|
|
||||||
<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">
|
<div class="agent-header">
|
||||||
<span class="agent-name">Claude Code CLI</span>
|
<span class="agent-name">Claude Code CLI</span>
|
||||||
<span class="agent-status">Active</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>
|
<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>
|
||||||
|
|
||||||
<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">
|
<div class="agent-header">
|
||||||
<span class="agent-name">Gemini CLI</span>
|
<span class="agent-name">Gemini CLI</span>
|
||||||
<span class="agent-status">Active</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>
|
<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>
|
||||||
|
|
||||||
<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">
|
<div class="agent-header">
|
||||||
<span class="agent-name">Qwen Coder CLI</span>
|
<span class="agent-name">Qwen Coder CLI</span>
|
||||||
<span class="agent-status">Active</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>
|
<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>
|
||||||
|
|
||||||
<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">
|
<div class="agent-header">
|
||||||
<span class="agent-name">Antigravity IDE</span>
|
<span class="agent-name">Antigravity IDE</span>
|
||||||
<span class="agent-status">Active</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, "'");
|
.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) {
|
function selectAgent(agent) {
|
||||||
// Remove active class from all cards
|
// 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
|
// Add active class to selected card
|
||||||
const selectedCard = document.getElementById('card-' + agent);
|
const selectedCard = document.getElementById('card-' + agent);
|
||||||
if (selectedCard) {
|
if (selectedCard) {
|
||||||
selectedCard.classList.add('active');
|
selectedCard.classList.add('active');
|
||||||
|
selectedCard.setAttribute('aria-checked', 'true');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update prompt text
|
// 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