Merge pull request #93 from nioasoft/feat/twitter-ui-theme

feat: Twitter-style UI theme with custom theme override system
This commit is contained in:
Leon van Zyl
2026-01-26 16:29:03 +02:00
committed by GitHub
6 changed files with 820 additions and 23 deletions

View File

@@ -1,12 +1,19 @@
# Optional: N8N webhook for progress notifications # Optional: N8N webhook for progress notifications
# PROGRESS_N8N_WEBHOOK_URL=https://your-n8n-instance.com/webhook/... # PROGRESS_N8N_WEBHOOK_URL=https://your-n8n-instance.com/webhook/...
# Playwright Browser Mode # Playwright Browser Configuration
# Controls whether Playwright runs Chrome in headless mode (no visible browser window). #
# - true: Browser runs in background, invisible (recommended for using PC while agent works) # PLAYWRIGHT_BROWSER: Which browser to use for testing
# - firefox: Lower CPU usage, recommended (default)
# - chrome: Google Chrome
# - webkit: Safari engine
# - msedge: Microsoft Edge
# PLAYWRIGHT_BROWSER=firefox
#
# PLAYWRIGHT_HEADLESS: Run browser without visible window
# - true: Browser runs in background, saves CPU (default)
# - false: Browser opens a visible window (useful for debugging) # - false: Browser opens a visible window (useful for debugging)
# Defaults to 'false' if not specified # PLAYWRIGHT_HEADLESS=true
# PLAYWRIGHT_HEADLESS=false
# GLM/Alternative API Configuration (Optional) # GLM/Alternative API Configuration (Optional)
# To use Zhipu AI's GLM models instead of Claude, uncomment and set these variables. # To use Zhipu AI's GLM models instead of Claude, uncomment and set these variables.

328
CUSTOM_UPDATES.md Normal file
View File

@@ -0,0 +1,328 @@
# Custom Updates - AutoCoder
This document tracks all customizations made to AutoCoder that deviate from the upstream repository. Reference this file before any updates to preserve these changes.
---
## Table of Contents
1. [UI Theme Customization](#1-ui-theme-customization)
2. [Playwright Browser Configuration](#2-playwright-browser-configuration)
3. [SQLite Robust Connection Handling](#3-sqlite-robust-connection-handling)
4. [Update Checklist](#update-checklist)
---
## 1. UI Theme Customization
### Overview
The UI has been customized from the default **neobrutalism** style to a clean **Twitter/Supabase-style** design.
**Design Changes:**
- No shadows
- Thin borders (1px)
- Rounded corners (1.3rem base)
- Blue accent color (Twitter blue)
- Clean typography (Open Sans)
### Modified Files
#### `ui/src/styles/custom-theme.css`
**Purpose:** Main theme override file that replaces neo design with clean Twitter style.
**Key Changes:**
- All `--shadow-neo-*` variables set to `none`
- All status colors (`pending`, `progress`, `done`) use Twitter blue
- Rounded corners: `--radius-neo-lg: 1.3rem`
- Font: Open Sans
- Removed all transform effects on hover
- Dark mode with proper contrast
**CSS Variables (Light Mode):**
```css
--color-neo-accent: oklch(0.6723 0.1606 244.9955); /* Twitter blue */
--color-neo-pending: oklch(0.6723 0.1606 244.9955);
--color-neo-progress: oklch(0.6723 0.1606 244.9955);
--color-neo-done: oklch(0.6723 0.1606 244.9955);
```
**CSS Variables (Dark Mode):**
```css
--color-neo-bg: oklch(0.08 0 0);
--color-neo-card: oklch(0.16 0.005 250);
--color-neo-border: oklch(0.30 0 0);
```
**How to preserve:** This file should NOT be overwritten. It loads after `globals.css` and overrides it.
---
#### `ui/src/components/KanbanColumn.tsx`
**Purpose:** Modified to support themeable kanban columns without inline styles.
**Changes:**
1. **colorMap changed from inline colors to CSS classes:**
```tsx
// BEFORE (original):
const colorMap = {
pending: 'var(--color-neo-pending)',
progress: 'var(--color-neo-progress)',
done: 'var(--color-neo-done)',
}
// AFTER (customized):
const colorMap = {
pending: 'kanban-header-pending',
progress: 'kanban-header-progress',
done: 'kanban-header-done',
}
```
2. **Column div uses CSS class instead of inline style:**
```tsx
// BEFORE:
<div className="neo-card overflow-hidden" style={{ borderColor: colorMap[color] }}>
// AFTER:
<div className={`neo-card overflow-hidden kanban-column ${colorMap[color]}`}>
```
3. **Header div simplified (removed duplicate color class):**
```tsx
// BEFORE:
<div className={`... ${colorMap[color]}`} style={{ backgroundColor: colorMap[color] }}>
// AFTER:
<div className="kanban-header px-4 py-3 border-b border-[var(--color-neo-border)]">
```
4. **Title text color:**
```tsx
// BEFORE:
text-[var(--color-neo-text-on-bright)]
// AFTER:
text-[var(--color-neo-text)]
```
---
## 2. Playwright Browser Configuration
### Overview
Changed default Playwright settings for better performance:
- **Default browser:** Firefox (lower CPU usage)
- **Default mode:** Headless (saves resources)
### Modified Files
#### `client.py`
**Changes:**
```python
# BEFORE:
DEFAULT_PLAYWRIGHT_HEADLESS = False
# AFTER:
DEFAULT_PLAYWRIGHT_HEADLESS = True
DEFAULT_PLAYWRIGHT_BROWSER = "firefox"
```
**New function added:**
```python
def get_playwright_browser() -> str:
"""
Get the browser to use for Playwright.
Options: chrome, firefox, webkit, msedge
Firefox is recommended for lower CPU usage.
"""
return os.getenv("PLAYWRIGHT_BROWSER", DEFAULT_PLAYWRIGHT_BROWSER).lower()
```
**Playwright args updated:**
```python
playwright_args = [
"@playwright/mcp@latest",
"--viewport-size", "1280x720",
"--browser", browser, # NEW: configurable browser
]
```
---
#### `.env.example`
**Updated documentation:**
```bash
# PLAYWRIGHT_BROWSER: Which browser to use for testing
# - firefox: Lower CPU usage, recommended (default)
# - chrome: Google Chrome
# - webkit: Safari engine
# - msedge: Microsoft Edge
# PLAYWRIGHT_BROWSER=firefox
# PLAYWRIGHT_HEADLESS: Run browser without visible window
# - true: Browser runs in background, saves CPU (default)
# - false: Browser opens a visible window (useful for debugging)
# PLAYWRIGHT_HEADLESS=true
```
---
## 3. SQLite Robust Connection Handling
### Overview
Added robust SQLite connection handling to prevent database corruption from concurrent access (MCP server, FastAPI server, progress tracking).
**Features Added:**
- WAL mode for better concurrency
- Busy timeout (30 seconds)
- Retry logic with exponential backoff
- Database health check endpoint
### Modified Files
#### `api/database.py`
**New functions added:**
```python
def get_robust_connection(db_path: str) -> sqlite3.Connection:
"""
Create a SQLite connection with robust settings:
- WAL mode for concurrent access
- 30 second busy timeout
- Foreign keys enabled
"""
@contextmanager
def robust_db_connection(db_path: str):
"""Context manager for robust database connections."""
def execute_with_retry(conn, sql, params=None, max_retries=3):
"""Execute SQL with exponential backoff retry for transient errors."""
def check_database_health(db_path: str) -> dict:
"""
Check database integrity and return health status.
Returns: {healthy: bool, message: str, details: dict}
"""
```
---
#### `progress.py`
**Changed from raw sqlite3 to robust connections:**
```python
# BEFORE:
conn = sqlite3.connect(db_path)
# AFTER:
from api.database import robust_db_connection, execute_with_retry
with robust_db_connection(db_path) as conn:
execute_with_retry(conn, sql, params)
```
---
#### `server/routers/projects.py`
**New endpoint added:**
```python
@router.get("/{project_name}/db-health")
async def get_database_health(project_name: str) -> DatabaseHealth:
"""
Check the health of the project's features database.
Useful for diagnosing corruption issues.
"""
```
---
#### `server/schemas.py`
**New schema added:**
```python
class DatabaseHealth(BaseModel):
healthy: bool
message: str
details: dict = {}
```
---
## Update Checklist
When updating AutoCoder from upstream, verify these items:
### UI Changes
- [ ] `ui/src/styles/custom-theme.css` is preserved
- [ ] `ui/src/components/KanbanColumn.tsx` changes are preserved
- [ ] Run `npm run build` in `ui/` directory
- [ ] Test both light and dark modes
### Backend Changes
- [ ] `client.py` - Playwright browser/headless defaults preserved
- [ ] `.env.example` - Documentation updates preserved
- [ ] `api/database.py` - Robust connection functions preserved
- [ ] `progress.py` - Uses robust_db_connection
- [ ] `server/routers/projects.py` - db-health endpoint preserved
- [ ] `server/schemas.py` - DatabaseHealth schema preserved
### General
- [ ] Test database operations under concurrent load
- [ ] Verify Playwright uses Firefox by default
- [ ] Check that browser runs headless by default
---
## Reverting to Defaults
### UI Only
```bash
rm ui/src/styles/custom-theme.css
git checkout ui/src/components/KanbanColumn.tsx
cd ui && npm run build
```
### Backend Only
```bash
git checkout client.py .env.example api/database.py progress.py
git checkout server/routers/projects.py server/schemas.py
```
---
## Files Summary
| File | Type | Change Description |
|------|------|-------------------|
| `ui/src/styles/custom-theme.css` | UI | Twitter-style theme |
| `ui/src/components/KanbanColumn.tsx` | UI | Themeable kanban columns |
| `client.py` | Backend | Firefox + headless defaults |
| `.env.example` | Config | Updated documentation |
| `api/database.py` | Backend | Robust SQLite connections |
| `progress.py` | Backend | Uses robust connections |
| `server/routers/projects.py` | Backend | db-health endpoint |
| `server/schemas.py` | Backend | DatabaseHealth schema |
---
## Last Updated
**Date:** January 2026
**Commits:**
- `1910b96` - SQLite robust connection handling
- `e014b04` - Custom theme override system

View File

@@ -21,9 +21,14 @@ from security import bash_security_hook
load_dotenv() load_dotenv()
# Default Playwright headless mode - can be overridden via PLAYWRIGHT_HEADLESS env var # Default Playwright headless mode - can be overridden via PLAYWRIGHT_HEADLESS env var
# When True, browser runs invisibly in background # When True, browser runs invisibly in background (default - saves CPU)
# When False, browser window is visible (default - useful for monitoring agent progress) # When False, browser window is visible (useful for monitoring agent progress)
DEFAULT_PLAYWRIGHT_HEADLESS = False DEFAULT_PLAYWRIGHT_HEADLESS = True
# Default browser for Playwright - can be overridden via PLAYWRIGHT_BROWSER env var
# Options: chrome, firefox, webkit, msedge
# Firefox is recommended for lower CPU usage
DEFAULT_PLAYWRIGHT_BROWSER = "firefox"
# Environment variables to pass through to Claude CLI for API configuration # Environment variables to pass through to Claude CLI for API configuration
# These allow using alternative API endpoints (e.g., GLM via z.ai) without # These allow using alternative API endpoints (e.g., GLM via z.ai) without
@@ -42,12 +47,27 @@ def get_playwright_headless() -> bool:
""" """
Get the Playwright headless mode setting. Get the Playwright headless mode setting.
Reads from PLAYWRIGHT_HEADLESS environment variable, defaults to False. Reads from PLAYWRIGHT_HEADLESS environment variable, defaults to True.
Returns True for headless mode (invisible browser), False for visible browser. Returns True for headless mode (invisible browser), False for visible browser.
""" """
value = os.getenv("PLAYWRIGHT_HEADLESS", "false").lower() value = os.getenv("PLAYWRIGHT_HEADLESS", str(DEFAULT_PLAYWRIGHT_HEADLESS).lower()).strip().lower()
# Accept various truthy/falsy values truthy = {"true", "1", "yes", "on"}
return value in ("true", "1", "yes", "on") falsy = {"false", "0", "no", "off"}
if value not in truthy | falsy:
print(f" - Warning: Invalid PLAYWRIGHT_HEADLESS='{value}', defaulting to {DEFAULT_PLAYWRIGHT_HEADLESS}")
return DEFAULT_PLAYWRIGHT_HEADLESS
return value in truthy
def get_playwright_browser() -> str:
"""
Get the browser to use for Playwright.
Reads from PLAYWRIGHT_BROWSER environment variable, defaults to firefox.
Options: chrome, firefox, webkit, msedge
Firefox is recommended for lower CPU usage.
"""
return os.getenv("PLAYWRIGHT_BROWSER", DEFAULT_PLAYWRIGHT_BROWSER).lower()
# Feature MCP tools for feature/test management # Feature MCP tools for feature/test management
@@ -228,10 +248,16 @@ def create_client(
} }
if not yolo_mode: if not yolo_mode:
# Include Playwright MCP server for browser automation (standard mode only) # Include Playwright MCP server for browser automation (standard mode only)
# Headless mode is configurable via PLAYWRIGHT_HEADLESS environment variable # Browser and headless mode configurable via environment variables
playwright_args = ["@playwright/mcp@latest", "--viewport-size", "1280x720"] browser = get_playwright_browser()
playwright_args = [
"@playwright/mcp@latest",
"--viewport-size", "1280x720",
"--browser", browser,
]
if get_playwright_headless(): if get_playwright_headless():
playwright_args.append("--headless") playwright_args.append("--headless")
print(f" - Browser: {browser} (headless={get_playwright_headless()})")
# Browser isolation for parallel execution # Browser isolation for parallel execution
# Each agent gets its own isolated browser context to prevent tab conflicts # Each agent gets its own isolated browser context to prevent tab conflicts

View File

@@ -18,9 +18,9 @@ interface KanbanColumnProps {
} }
const colorMap = { const colorMap = {
pending: 'var(--color-neo-pending)', pending: 'kanban-header-pending',
progress: 'var(--color-neo-progress)', progress: 'kanban-header-progress',
done: 'var(--color-neo-done)', done: 'kanban-header-done',
} }
export function KanbanColumn({ export function KanbanColumn({
@@ -43,18 +43,16 @@ export function KanbanColumn({
) )
return ( return (
<div <div
className="neo-card overflow-hidden" className={`neo-card overflow-hidden kanban-column ${colorMap[color]}`}
style={{ borderColor: colorMap[color] }}
> >
{/* Header */} {/* Header */}
<div <div
className="px-4 py-3 border-b-3 border-[var(--color-neo-border)]" className="kanban-header px-4 py-3 border-b border-[var(--color-neo-border)]"
style={{ backgroundColor: colorMap[color] }}
> >
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<h2 className="font-display text-lg font-bold uppercase flex items-center gap-2 text-[var(--color-neo-text-on-bright)]"> <h2 className="font-display text-lg font-semibold flex items-center gap-2 text-[var(--color-neo-text)]">
{title} {title}
<span className="neo-badge bg-[var(--color-neo-card)] text-[var(--color-neo-text)]">{count}</span> <span className="neo-badge bg-[var(--color-neo-neutral-100)] text-[var(--color-neo-text-secondary)]">{count}</span>
</h2> </h2>
{(onAddFeature || onExpandProject) && ( {(onAddFeature || onExpandProject) && (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">

View File

@@ -3,6 +3,7 @@ import { createRoot } from 'react-dom/client'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import App from './App' import App from './App'
import './styles/globals.css' import './styles/globals.css'
import './styles/custom-theme.css' // Custom theme overrides (safe from upstream conflicts)
const queryClient = new QueryClient({ const queryClient = new QueryClient({
defaultOptions: { defaultOptions: {

View File

@@ -0,0 +1,437 @@
/*
* Clean Twitter-Style Theme
* =========================
* Based on user's exact design system values
*/
:root {
/* Core colors */
--color-neo-bg: oklch(1.0000 0 0);
--color-neo-card: oklch(0.9784 0.0011 197.1387);
--color-neo-text: oklch(0.1884 0.0128 248.5103);
--color-neo-text-secondary: oklch(0.1884 0.0128 248.5103);
--color-neo-text-muted: oklch(0.5637 0.0078 247.9662);
--color-neo-text-on-bright: oklch(1.0000 0 0);
/* Primary accent - Twitter blue */
--color-neo-accent: oklch(0.6723 0.1606 244.9955);
/* Status colors - all use accent blue except danger */
--color-neo-pending: oklch(0.6723 0.1606 244.9955);
--color-neo-progress: oklch(0.6723 0.1606 244.9955);
--color-neo-done: oklch(0.6723 0.1606 244.9955);
--color-neo-danger: oklch(0.6188 0.2376 25.7658);
/* Borders and neutrals */
--color-neo-border: oklch(0.9317 0.0118 231.6594);
--color-neo-neutral-50: oklch(0.9809 0.0025 228.7836);
--color-neo-neutral-100: oklch(0.9392 0.0166 250.8453);
--color-neo-neutral-200: oklch(0.9222 0.0013 286.3737);
--color-neo-neutral-300: oklch(0.9317 0.0118 231.6594);
/* No shadows */
--shadow-neo-sm: none;
--shadow-neo-md: none;
--shadow-neo-lg: none;
--shadow-neo-xl: none;
--shadow-neo-left: none;
--shadow-neo-inset: none;
/* Typography */
--font-neo-sans: Open Sans, sans-serif;
--font-neo-mono: Menlo, monospace;
/* Radius - 1.3rem base */
--radius-neo-sm: calc(1.3rem - 4px);
--radius-neo-md: calc(1.3rem - 2px);
--radius-neo-lg: 1.3rem;
--radius-neo-xl: calc(1.3rem + 4px);
}
.dark {
/* Core colors - dark mode (Twitter dark style) */
--color-neo-bg: oklch(0.08 0 0);
--color-neo-card: oklch(0.16 0.005 250);
--color-neo-text: oklch(0.95 0 0);
--color-neo-text-secondary: oklch(0.75 0 0);
--color-neo-text-muted: oklch(0.55 0 0);
--color-neo-text-on-bright: oklch(1.0 0 0);
/* Primary accent */
--color-neo-accent: oklch(0.6692 0.1607 245.0110);
/* Status colors - all use accent blue except danger */
--color-neo-pending: oklch(0.6692 0.1607 245.0110);
--color-neo-progress: oklch(0.6692 0.1607 245.0110);
--color-neo-done: oklch(0.6692 0.1607 245.0110);
--color-neo-danger: oklch(0.6188 0.2376 25.7658);
/* Borders and neutrals - better contrast */
--color-neo-border: oklch(0.30 0 0);
--color-neo-neutral-50: oklch(0.20 0 0);
--color-neo-neutral-100: oklch(0.25 0.01 250);
--color-neo-neutral-200: oklch(0.22 0 0);
--color-neo-neutral-300: oklch(0.30 0 0);
/* No shadows */
--shadow-neo-sm: none;
--shadow-neo-md: none;
--shadow-neo-lg: none;
--shadow-neo-xl: none;
--shadow-neo-left: none;
--shadow-neo-inset: none;
}
/* ===== GLOBAL OVERRIDES ===== */
* {
box-shadow: none !important;
}
/* ===== CARDS ===== */
.neo-card,
[class*="neo-card"] {
border: 1px solid var(--color-neo-border) !important;
box-shadow: none !important;
transform: none !important;
border-radius: var(--radius-neo-lg) !important;
background-color: var(--color-neo-card) !important;
}
.neo-card:hover,
[class*="neo-card"]:hover {
transform: none !important;
box-shadow: none !important;
}
/* ===== BUTTONS ===== */
.neo-btn,
[class*="neo-btn"],
button {
border-width: 1px !important;
box-shadow: none !important;
text-transform: none !important;
font-weight: 500 !important;
transform: none !important;
border-radius: var(--radius-neo-lg) !important;
font-family: var(--font-neo-sans) !important;
}
.neo-btn:hover,
[class*="neo-btn"]:hover,
button:hover {
transform: none !important;
box-shadow: none !important;
}
.neo-btn:active,
[class*="neo-btn"]:active {
transform: none !important;
}
/* Primary button */
.neo-btn-primary {
background-color: var(--color-neo-accent) !important;
border-color: var(--color-neo-accent) !important;
color: white !important;
}
/* Success button - use accent blue instead of green */
.neo-btn-success {
background-color: var(--color-neo-accent) !important;
border-color: var(--color-neo-accent) !important;
color: white !important;
}
/* Danger button - subtle red */
.neo-btn-danger {
background-color: var(--color-neo-danger) !important;
border-color: var(--color-neo-danger) !important;
color: white !important;
}
/* ===== INPUTS ===== */
.neo-input,
.neo-textarea,
input,
textarea,
select {
border: 1px solid var(--color-neo-border) !important;
box-shadow: none !important;
border-radius: var(--radius-neo-md) !important;
background-color: var(--color-neo-neutral-50) !important;
}
.neo-input:focus,
.neo-textarea:focus,
input:focus,
textarea:focus,
select:focus {
box-shadow: none !important;
border-color: var(--color-neo-accent) !important;
outline: none !important;
}
/* ===== KEYBOARD ACCESSIBILITY ===== */
/* Focus-visible styles for keyboard navigation */
.neo-btn:focus-visible,
[class*="neo-btn"]:focus-visible,
button:focus-visible {
outline: 2px solid var(--color-neo-accent) !important;
outline-offset: 2px !important;
}
.neo-input:focus-visible,
.neo-textarea:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible {
outline: 2px solid var(--color-neo-accent) !important;
outline-offset: 2px !important;
border-color: var(--color-neo-accent) !important;
}
a:focus-visible,
[role="button"]:focus-visible,
[tabindex]:focus-visible {
outline: 2px solid var(--color-neo-accent) !important;
outline-offset: 2px !important;
}
/* ===== BADGES ===== */
.neo-badge,
[class*="neo-badge"] {
border: 1px solid var(--color-neo-border) !important;
box-shadow: none !important;
border-radius: var(--radius-neo-lg) !important;
font-weight: 500 !important;
text-transform: none !important;
}
/* ===== PROGRESS BAR ===== */
.neo-progress {
border: none !important;
box-shadow: none !important;
border-radius: var(--radius-neo-lg) !important;
background-color: var(--color-neo-neutral-100) !important;
overflow: hidden !important;
height: 0.75rem !important;
}
.neo-progress-fill {
background-color: var(--color-neo-accent) !important;
border-radius: var(--radius-neo-lg) !important;
}
.neo-progress-fill::after {
display: none !important;
}
/* ===== KANBAN COLUMNS ===== */
.kanban-column {
border: 1px solid var(--color-neo-border) !important;
border-radius: var(--radius-neo-lg) !important;
overflow: hidden;
background-color: var(--color-neo-bg) !important;
border-left: none !important;
}
/* Left accent border on the whole column */
.kanban-column.kanban-header-pending {
border-left: 3px solid var(--color-neo-accent) !important;
}
.kanban-column.kanban-header-progress {
border-left: 3px solid var(--color-neo-accent) !important;
}
.kanban-column.kanban-header-done {
border-left: 3px solid var(--color-neo-accent) !important;
}
.kanban-header {
background-color: var(--color-neo-card) !important;
border-bottom: 1px solid var(--color-neo-border) !important;
border-left: none !important;
}
/* ===== MODALS & DROPDOWNS ===== */
.neo-modal,
[class*="neo-modal"],
[role="dialog"] {
border: 1px solid var(--color-neo-border) !important;
border-radius: var(--radius-neo-xl) !important;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.1) !important;
}
.neo-dropdown,
[class*="dropdown"],
[role="menu"],
[data-radix-popper-content-wrapper] {
border: 1px solid var(--color-neo-border) !important;
border-radius: var(--radius-neo-lg) !important;
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.08) !important;
}
/* ===== STATUS BADGES ===== */
[class*="bg-neo-pending"],
.bg-\[var\(--color-neo-pending\)\] {
background-color: var(--color-neo-neutral-100) !important;
color: var(--color-neo-text-secondary) !important;
}
[class*="bg-neo-progress"],
.bg-\[var\(--color-neo-progress\)\] {
background-color: oklch(0.9392 0.0166 250.8453) !important;
color: var(--color-neo-accent) !important;
}
[class*="bg-neo-done"],
.bg-\[var\(--color-neo-done\)\] {
background-color: oklch(0.9392 0.0166 250.8453) !important;
color: var(--color-neo-accent) !important;
}
/* ===== REMOVE NEO EFFECTS ===== */
[class*="shadow-neo"],
[class*="shadow-"] {
box-shadow: none !important;
}
[class*="hover:translate"],
[class*="hover:-translate"],
[class*="translate-x"],
[class*="translate-y"] {
transform: none !important;
}
/* ===== TEXT STYLING ===== */
h1, h2, h3, h4, h5, h6,
[class*="heading"],
[class*="title"],
[class*="font-display"] {
text-transform: none !important;
font-family: var(--font-neo-sans) !important;
}
.uppercase {
text-transform: none !important;
}
strong, b,
[class*="font-bold"],
[class*="font-black"] {
font-weight: 600 !important;
}
/* ===== SPECIFIC ELEMENT FIXES ===== */
/* Green badges should use accent color */
[class*="bg-green"],
[class*="bg-emerald"],
[class*="bg-lime"] {
background-color: oklch(0.9392 0.0166 250.8453) !important;
color: var(--color-neo-accent) !important;
}
/* Category badges */
[class*="FUNCTIONAL"],
[class*="functional"] {
background-color: oklch(0.9392 0.0166 250.8453) !important;
color: var(--color-neo-accent) !important;
}
/* Live/Status indicators - use accent instead of green */
.text-\[var\(--color-neo-done\)\] {
color: var(--color-neo-accent) !important;
}
/* Override any remaining borders to be thin */
[class*="border-3"],
[class*="border-b-3"] {
border-width: 1px !important;
}
/* ===== DARK MODE SPECIFIC FIXES ===== */
.dark .neo-card,
.dark [class*="neo-card"] {
background-color: var(--color-neo-card) !important;
border-color: var(--color-neo-border) !important;
}
.dark .kanban-column {
background-color: var(--color-neo-card) !important;
}
.dark .kanban-header {
background-color: var(--color-neo-neutral-50) !important;
}
/* Feature cards in dark mode */
.dark .neo-card .neo-card {
background-color: var(--color-neo-neutral-50) !important;
}
/* Badges in dark mode - lighter background for visibility */
.dark .neo-badge,
.dark [class*="neo-badge"] {
background-color: var(--color-neo-neutral-100) !important;
color: var(--color-neo-text) !important;
border-color: var(--color-neo-border) !important;
}
/* Status badges in dark mode */
.dark [class*="bg-neo-done"],
.dark .bg-\[var\(--color-neo-done\)\] {
background-color: oklch(0.25 0.05 245) !important;
color: var(--color-neo-accent) !important;
}
.dark [class*="bg-neo-progress"],
.dark .bg-\[var\(--color-neo-progress\)\] {
background-color: oklch(0.25 0.05 245) !important;
color: var(--color-neo-accent) !important;
}
/* Green badges in dark mode */
.dark [class*="bg-green"],
.dark [class*="bg-emerald"],
.dark [class*="bg-lime"] {
background-color: oklch(0.25 0.05 245) !important;
color: var(--color-neo-accent) !important;
}
/* Category badges in dark mode */
.dark [class*="FUNCTIONAL"],
.dark [class*="functional"] {
background-color: oklch(0.25 0.05 245) !important;
color: var(--color-neo-accent) !important;
}
/* Buttons in dark mode - better visibility */
.dark .neo-btn,
.dark button {
border-color: var(--color-neo-border) !important;
}
.dark .neo-btn-primary,
.dark .neo-btn-success {
background-color: var(--color-neo-accent) !important;
border-color: var(--color-neo-accent) !important;
color: white !important;
}
/* Toggle buttons - fix "Graph" visibility */
.dark [class*="text-neo-text"] {
color: var(--color-neo-text) !important;
}
/* Inputs in dark mode */
.dark input,
.dark textarea,
.dark select {
background-color: var(--color-neo-neutral-50) !important;
border-color: var(--color-neo-border) !important;
color: var(--color-neo-text) !important;
}