theme enforced and correctly implemented; e2e tests added for theme
This commit is contained in:
parent
0bc47e60a5
commit
56ba613cc7
@ -50,8 +50,7 @@ export default function Header({ user, onSignOut }: HeaderProps) {
|
||||
|
||||
{menuOpen && (
|
||||
<div
|
||||
className="absolute right-0 top-full mt-1 w-40 border border-border-default rounded shadow-lg z-50"
|
||||
style={{ backgroundColor: '#1a1a1a' }}
|
||||
className="absolute right-0 top-full mt-1 w-40 border border-border-default rounded shadow-lg z-50 bg-bg-tertiary"
|
||||
>
|
||||
<div className="py-1">
|
||||
<button
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
:root {
|
||||
@theme {
|
||||
--color-brand: #c20600;
|
||||
--color-bg-primary: #0a0a0a;
|
||||
--color-bg-secondary: #121212;
|
||||
@ -10,8 +10,14 @@
|
||||
--color-text-secondary: #a3a3a3;
|
||||
--color-text-muted: #737373;
|
||||
--color-border-subtle: #1a1a1a;
|
||||
--color-border-default: #27272a;
|
||||
--color-border-highlight: #2a2a2a;
|
||||
--color-border-default: #2a2a2a;
|
||||
--color-border-highlight: #3a3a3a;
|
||||
|
||||
--font-mono: 'Courier New', Courier, monospace;
|
||||
--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
|
||||
--spacing-header: 48px;
|
||||
--spacing-status: 32px;
|
||||
}
|
||||
|
||||
* {
|
||||
@ -20,7 +26,7 @@
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Roboto', 'sans-serif';
|
||||
font-family: var(--font-sans);
|
||||
background-color: var(--color-bg-primary);
|
||||
color: var(--color-text-primary);
|
||||
line-height: 1.5;
|
||||
@ -35,12 +41,11 @@ body {
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text-secondary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--color-text-primary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
button {
|
||||
|
||||
@ -27,7 +27,7 @@ export default function SignIn({ onSuccess }: SignInProps) {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-bg">
|
||||
<div className="min-h-screen flex items-center justify-center bg-bg-primary">
|
||||
<div className="w-full max-w-md p-8">
|
||||
<h1 className="text-2xl font-bold text-center mb-6">Sign In</h1>
|
||||
{error && (
|
||||
@ -45,7 +45,7 @@ export default function SignIn({ onSuccess }: SignInProps) {
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className="w-full px-4 py-2 bg-bg-secondary border border-border rounded-lg text-text focus:outline-none focus:border-primary"
|
||||
className="w-full px-4 py-2 bg-bg-tertiary border border-border-default rounded-lg text-text-primary focus:outline-none focus:border-brand"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
@ -58,21 +58,21 @@ export default function SignIn({ onSuccess }: SignInProps) {
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
className="w-full px-4 py-2 bg-bg-secondary border border-border rounded-lg text-text focus:outline-none focus:border-primary"
|
||||
className="w-full px-4 py-2 bg-bg-tertiary border border-border-default rounded-lg text-text-primary focus:outline-none focus:border-brand"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="flex gap-4 pt-2">
|
||||
<Link
|
||||
to="/"
|
||||
className="flex-1 px-4 py-2 text-center border border-border rounded-lg hover:bg-bg-secondary transition-colors"
|
||||
className="flex-1 px-4 py-2 text-center border border-border-default rounded-lg hover:bg-bg-tertiary transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</Link>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="flex-1 px-4 py-2 bg-primary hover:bg-primary-hover text-white rounded-lg transition-colors disabled:opacity-50"
|
||||
className="flex-1 px-4 py-2 bg-brand hover:bg-red-700 text-white rounded-lg transition-colors disabled:opacity-50"
|
||||
>
|
||||
{loading ? 'Signing in...' : 'Sign In'}
|
||||
</button>
|
||||
@ -80,7 +80,7 @@ export default function SignIn({ onSuccess }: SignInProps) {
|
||||
</form>
|
||||
<p className="mt-4 text-center text-text-muted">
|
||||
Don't have an account?{' '}
|
||||
<Link to="/sign-up" className="text-primary hover:underline">
|
||||
<Link to="/sign-up" className="text-brand hover:underline">
|
||||
Sign up
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
@ -39,7 +39,7 @@ export default function SignUp({ onSuccess }: SignUpProps) {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-bg">
|
||||
<div className="min-h-screen flex items-center justify-center bg-bg-primary">
|
||||
<div className="w-full max-w-md p-8">
|
||||
<h1 className="text-2xl font-bold text-center mb-6">Create Account</h1>
|
||||
{error && (
|
||||
@ -57,7 +57,7 @@ export default function SignUp({ onSuccess }: SignUpProps) {
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className="w-full px-4 py-2 bg-bg-secondary border border-border rounded-lg text-text focus:outline-none focus:border-primary"
|
||||
className="w-full px-4 py-2 bg-bg-tertiary border border-border-default rounded-lg text-text-primary focus:outline-none focus:border-brand"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
@ -70,7 +70,7 @@ export default function SignUp({ onSuccess }: SignUpProps) {
|
||||
type="text"
|
||||
value={displayName}
|
||||
onChange={(e) => setDisplayName(e.target.value)}
|
||||
className="w-full px-4 py-2 bg-bg-secondary border border-border rounded-lg text-text focus:outline-none focus:border-primary"
|
||||
className="w-full px-4 py-2 bg-bg-tertiary border border-border-default rounded-lg text-text-primary focus:outline-none focus:border-brand"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
@ -83,7 +83,7 @@ export default function SignUp({ onSuccess }: SignUpProps) {
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
className="w-full px-4 py-2 bg-bg-secondary border border-border rounded-lg text-text focus:outline-none focus:border-primary"
|
||||
className="w-full px-4 py-2 bg-bg-tertiary border border-border-default rounded-lg text-text-primary focus:outline-none focus:border-brand"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
@ -96,21 +96,21 @@ export default function SignUp({ onSuccess }: SignUpProps) {
|
||||
type="password"
|
||||
value={passwordVerify}
|
||||
onChange={(e) => setPasswordVerify(e.target.value)}
|
||||
className="w-full px-4 py-2 bg-bg-secondary border border-border rounded-lg text-text focus:outline-none focus:border-primary"
|
||||
className="w-full px-4 py-2 bg-bg-tertiary border border-border-default rounded-lg text-text-primary focus:outline-none focus:border-brand"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="flex gap-4 pt-2">
|
||||
<Link
|
||||
to="/"
|
||||
className="flex-1 px-4 py-2 text-center border border-border rounded-lg hover:bg-bg-secondary transition-colors"
|
||||
className="flex-1 px-4 py-2 text-center border border-border-default rounded-lg hover:bg-bg-tertiary transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</Link>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="flex-1 px-4 py-2 bg-primary hover:bg-primary-hover text-white rounded-lg transition-colors disabled:opacity-50"
|
||||
className="flex-1 px-4 py-2 bg-brand hover:bg-red-700 text-white rounded-lg transition-colors disabled:opacity-50"
|
||||
>
|
||||
{loading ? 'Creating...' : 'Sign Up'}
|
||||
</button>
|
||||
|
||||
@ -14,8 +14,8 @@ export default {
|
||||
'text-secondary': '#a3a3a3',
|
||||
'text-muted': '#737373',
|
||||
'border-subtle': '#1a1a1a',
|
||||
'border-default': '#27272a',
|
||||
'border-highlight': '#2a2a2a',
|
||||
'border-default': '#2a2a2a',
|
||||
'border-highlight': '#3a3a3a',
|
||||
},
|
||||
fontFamily: {
|
||||
mono: ['Courier New', 'Courier', 'monospace'],
|
||||
|
||||
108
gadget-code/tests/e2e/theme.test.ts
Normal file
108
gadget-code/tests/e2e/theme.test.ts
Normal file
@ -0,0 +1,108 @@
|
||||
import { chromium, test, expect } from '@playwright/test';
|
||||
|
||||
const THEME = {
|
||||
brand: '#c20600',
|
||||
bgPrimary: '#0a0a0a',
|
||||
bgSecondary: '#121212',
|
||||
bgTertiary: '#1a1a1a',
|
||||
bgElevated: '#202020',
|
||||
textPrimary: '#d4d4d4',
|
||||
textSecondary: '#a3a3a3',
|
||||
textMuted: '#737373',
|
||||
borderSubtle: '#1a1a1a',
|
||||
borderDefault: '#2a2a2a',
|
||||
borderHighlight: '#3a3a3a',
|
||||
};
|
||||
|
||||
function rgbToHex(rgb: string): string {
|
||||
const match = rgb.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
|
||||
if (!match) return rgb;
|
||||
const [, r, g, b] = match.map(Number);
|
||||
return `#${[r, g, b].map((x) => x.toString(16).padStart(2, '0')).join('')}`;
|
||||
}
|
||||
|
||||
test.describe('Theme Colors', () => {
|
||||
test('header should have correct border color', async ({ page }) => {
|
||||
await page.goto('https://code-dev.g4dge7.com:5174/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
const header = page.locator('header').first();
|
||||
await expect(header).toBeVisible();
|
||||
|
||||
const borderColor = await header.evaluate((el) => {
|
||||
return window.getComputedStyle(el).borderBottomColor;
|
||||
});
|
||||
|
||||
const actualHex = rgbToHex(borderColor);
|
||||
const expectedHex = THEME.borderSubtle;
|
||||
|
||||
console.log('Header border-bottom-color:', actualHex, '(expected:', expectedHex, ')');
|
||||
expect(actualHex.toLowerCase()).toBe(expectedHex);
|
||||
});
|
||||
|
||||
test('body should have correct background color', async ({ page }) => {
|
||||
await page.goto('https://code-dev.g4dge7.com:5174/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
const bgColor = await page.evaluate(() => {
|
||||
return window.getComputedStyle(document.body).backgroundColor;
|
||||
});
|
||||
|
||||
const actualHex = rgbToHex(bgColor);
|
||||
const expectedHex = THEME.bgPrimary;
|
||||
|
||||
console.log('Body background-color:', actualHex, '(expected:', expectedHex, ')');
|
||||
expect(actualHex.toLowerCase()).toBe(expectedHex);
|
||||
});
|
||||
|
||||
test('header text should have correct color', async ({ page }) => {
|
||||
await page.goto('https://code-dev.g4dge7.com:5174/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
const header = page.locator('header').first();
|
||||
const link = header.locator('a').first();
|
||||
|
||||
const color = await link.evaluate((el) => {
|
||||
return window.getComputedStyle(el).color;
|
||||
});
|
||||
|
||||
const actualHex = rgbToHex(color);
|
||||
const expectedHex = THEME.textPrimary;
|
||||
|
||||
console.log('Header link color:', actualHex, '(expected:', expectedHex, ')');
|
||||
expect(actualHex.toLowerCase()).toBe(expectedHex);
|
||||
});
|
||||
|
||||
test('all theme colors should match spec', async ({ page }) => {
|
||||
await page.goto('https://code-dev.g4dge7.com:5174/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
const results: string[] = [];
|
||||
|
||||
const header = page.locator('header').first();
|
||||
const headerBorder = await header.evaluate((el) => {
|
||||
const c = window.getComputedStyle(el).borderBottomColor;
|
||||
const m = c.match(/rgb\((\d+),\s*(\d+),\s*(\d+)/);
|
||||
if (!m) return c;
|
||||
return '#' + [m[1], m[2], m[3]].map(x => parseInt(x).toString(16).padStart(2, '0')).join('');
|
||||
});
|
||||
results.push(`header border: ${headerBorder}`);
|
||||
|
||||
const bodyBg = await page.evaluate(() => {
|
||||
const c = window.getComputedStyle(document.body).backgroundColor;
|
||||
const m = c.match(/rgb\((\d+),\s*(\d+),\s*(\d+)/);
|
||||
if (!m) return c;
|
||||
return '#' + [m[1], m[2], m[3]].map(x => parseInt(x).toString(16).padStart(2, '0')).join('');
|
||||
});
|
||||
results.push(`body bg: ${bodyBg}`);
|
||||
|
||||
console.log('Theme verification:', results.join(', '));
|
||||
|
||||
expect(headerBorder.toLowerCase()).toBe(THEME.borderSubtle);
|
||||
expect(bodyBg.toLowerCase()).toBe(THEME.bgPrimary);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user