From e077c36e1c96832631b1a497d5d7272dc5e8062e Mon Sep 17 00:00:00 2001
From: zack34567
Date: Sat, 23 May 2026 10:04:14 +0530
Subject: [PATCH] fix: improve color palette and visual consistency (closes
#616)
---
src/app/contact/page.tsx | 12 ++---
src/app/globals.css | 65 +++++++++++++++++++++++-----
src/app/layout.tsx | 4 +-
src/app/page.tsx | 2 +-
src/components/AudioSpeedControl.tsx | 4 +-
src/components/DownloadResult.tsx | 14 +++---
src/components/ExportSettings.tsx | 4 +-
src/components/FileUpload.tsx | 24 +++++-----
src/components/Footer.tsx | 20 ++++-----
src/components/ImageOverlay.tsx | 42 +++++++++---------
src/components/ScrollToTop.tsx | 4 +-
src/components/ThemeToggle.tsx | 7 +--
src/components/ThumbnailStrip.tsx | 56 +++++++++++++-----------
src/components/TrimControl.tsx | 10 +++--
src/components/VideoEditor.tsx | 10 ++---
src/components/VideoPreview.tsx | 30 ++++++-------
src/components/WaveformCanvas.tsx | 4 +-
src/components/components.tsx | 48 +++++++++-----------
tailwind.config.ts | 22 +++++-----
vitest.config.ts | 5 +++
vitest.setup.ts | 14 ++++++
21 files changed, 234 insertions(+), 167 deletions(-)
diff --git a/src/app/contact/page.tsx b/src/app/contact/page.tsx
index 224ae2f1..13b04261 100644
--- a/src/app/contact/page.tsx
+++ b/src/app/contact/page.tsx
@@ -6,11 +6,11 @@ export const metadata: Metadata = {
};
export default function ContactPage() {
return (
-
+
← Back to Reframe
@@ -27,11 +27,11 @@ export default function ContactPage() {
href="https://github.com/magic-peach/reframe/issues"
target="_blank"
rel="noopener noreferrer"
- className="text-lg font-semibold underline hover:opacity-80 transition-opacity"
+ className="text-lg font-semibold text-[var(--accent)] underline hover:text-[var(--accent-hover)] transition-colors"
>
GitHub Issues
-
For bug reports and feature requests.
+
For bug reports and feature requests.
@@ -39,11 +39,11 @@ export default function ContactPage() {
href="https://github.com/magic-peach/reframe/discussions"
target="_blank"
rel="noopener noreferrer"
- className="text-lg font-semibold underline hover:opacity-80 transition-opacity"
+ className="text-lg font-semibold text-[var(--accent)] underline hover:text-[var(--accent-hover)] transition-colors"
>
GitHub Discussions
-
For questions, ideas, and general help.
+
For questions, ideas, and general help.
diff --git a/src/app/globals.css b/src/app/globals.css
index 0bab4371..408e2d8e 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -11,6 +11,9 @@
--muted: #64748b;
--accent: #3b82f6;
--accent-hover: #1d4ed8;
+ --accent-muted: rgba(59, 130, 246, 0.12);
+ --radius: 10px;
+ --shadow: 0 2px 12px rgba(15, 23, 42, 0.12);
--film-600: #e63946;
--film-400: #ff6b6b;
--warning: #f59e0b;
@@ -22,13 +25,16 @@
/* ── Dark mode tokens ── */
.dark {
- --bg: #0f172a;
- --surface: #1e293b;
- --border: #334155;
- --text: #f1f5f9;
- --muted: #94a3b8;
- --accent: #3b82f6;
- --accent-hover: #2563eb;
+ --bg: #0f1117;
+ --surface: #1a1d27;
+ --border: #2e3147;
+ --text: #f0f0f5;
+ --muted: #8b8fa8;
+ --accent: #4f6ef7;
+ --accent-hover: #3a57d4;
+ --accent-muted: rgba(79, 110, 247, 0.12);
+ --radius: 10px;
+ --shadow: 0 2px 12px rgba(0, 0, 0, 0.3);
--warning: #fbbf24;
--error: #f87171;
--error-bg: #7f1d1d;
@@ -47,6 +53,9 @@
--accent: #FFFF00;
--accent-hover: #FFFF00;
+ --accent-muted: rgba(255, 255, 0, 0.22);
+ --radius: 10px;
+ --shadow: none;
--error: #FF6666;
--success: #66FF66;
@@ -82,9 +91,45 @@
body {
background-color: var(--bg);
color: var(--text);
+ line-height: 1.6;
transition: background-color 0.3s ease, color 0.3s ease;
}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ color: var(--text);
+ font-weight: 700;
+}
+
+p,
+li {
+ color: color-mix(in srgb, var(--text) 95%, transparent);
+}
+
+button {
+ transition: all 0.2s ease;
+}
+
+input,
+select,
+textarea {
+ background: var(--bg);
+ border: 1px solid var(--border);
+ color: var(--text);
+}
+
+input:focus,
+select:focus,
+textarea:focus {
+ border-color: var(--accent);
+ box-shadow: 0 0 0 3px var(--accent-muted);
+ outline: none;
+}
+
/* Smooth transitions for all themed elements */
*,
*::before,
@@ -95,7 +140,7 @@ body {
}
:focus-visible {
- outline: 2px solid var(--accent);
- outline-offset: 2px;
- border-radius: 4px;
+ outline: 0;
+ box-shadow: 0 0 0 3px var(--accent-muted);
+ border-radius: var(--radius);
}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 593f68c8..129528b2 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -70,7 +70,7 @@ export default function RootLayout({
Skip to main content
@@ -78,7 +78,7 @@ export default function RootLayout({
diff --git a/src/app/page.tsx b/src/app/page.tsx
index 5245ade9..381fbd80 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -8,7 +8,7 @@ export default function Home() {
href="https://github.com/magic-peach/reframe"
target="_blank"
rel="noopener noreferrer"
- className="hidden min-[300px]:flex fixed top-4 right-16 z-50 items-center gap-1.5 px-3 py-1.5 rounded-lg border border-[var(--border)] bg-[var(--surface)] text-[10px] font-heading font-semibold uppercase tracking-wider transition-all duration-200 ease-in-out hover:scale-105 hover:shadow-[0_0_10px_rgba(255,255,255,0.15)] hover:bg-white/10"
+ className="hidden min-[300px]:flex fixed top-4 right-16 z-50 items-center gap-1.5 px-3 py-1.5 rounded-lg border border-[var(--border)] bg-[var(--surface)] text-[10px] font-heading font-semibold uppercase tracking-wider transition-all duration-200 ease-in-out hover:scale-105 hover:border-[var(--accent)] hover:bg-[var(--accent-muted)] hover:shadow-[var(--shadow)]"
>
⭐ Star on GitHub
diff --git a/src/components/AudioSpeedControl.tsx b/src/components/AudioSpeedControl.tsx
index bbeb93c8..dd6c6007 100644
--- a/src/components/AudioSpeedControl.tsx
+++ b/src/components/AudioSpeedControl.tsx
@@ -146,7 +146,7 @@ export default function AudioSpeedControl({ recipe, onChange }: Props) {
)}
{recipe.keepAudio && (recipe.trimStart !== 0 || recipe.trimEnd !== null) && (
-
+
Note: If audio doesn't start within the selected range, the output will be silent.
@@ -155,4 +155,4 @@ export default function AudioSpeedControl({ recipe, onChange }: Props) {
)}
);
-}
\ No newline at end of file
+}
diff --git a/src/components/DownloadResult.tsx b/src/components/DownloadResult.tsx
index 1f61081c..bfc039ff 100644
--- a/src/components/DownloadResult.tsx
+++ b/src/components/DownloadResult.tsx
@@ -80,7 +80,7 @@ export default function DownloadResult({ result, onReset, soundOnCompletion, onT
Filename
-
= 100 ? "text-red-500 font-medium" : "text-[var(--muted)]")}>
+ = 100 ? "text-[var(--error)] font-medium" : "text-[var(--muted)]")}>
{100 - name.length} chars remaining
@@ -93,7 +93,7 @@ export default function DownloadResult({ result, onReset, soundOnCompletion, onT
maxLength={100}
className={cn(
"flex-1 px-3 py-2.5 bg-[var(--bg)] border rounded-lg text-sm transition-colors text-[var(--text)] placeholder:text-[var(--muted)]",
- !isValid && name.length > 0 ? "border-red-500 focus:outline-red-500 focus:ring-1 focus:ring-red-500" : "border-[var(--border)] focus:outline-film-500"
+ !isValid && name.length > 0 ? "border-[var(--error)] focus:outline-[var(--error)] focus:ring-1 focus:ring-[var(--error)]" : "border-[var(--border)] focus:outline-[var(--accent)]"
)}
placeholder="Enter filename"
/>
@@ -102,7 +102,7 @@ export default function DownloadResult({ result, onReset, soundOnCompletion, onT
{!isValid && name.length > 0 && (
-
+
Filename contains invalid characters (\ / : * ? " < > |)
@@ -116,7 +116,7 @@ export default function DownloadResult({ result, onReset, soundOnCompletion, onT
className={cn(
"flex-1 min-w-[10rem] flex items-center justify-center gap-2 py-3 text-sm font-heading font-bold uppercase tracking-wide rounded-lg transition-all",
isValid
- ? "bg-film-600 text-white hover:bg-film-700 hover:scale-[1.01] active:scale-[0.99] cursor-pointer"
+ ? "bg-[var(--accent)] text-white hover:bg-[var(--accent-hover)] hover:scale-[1.02] active:scale-[0.99] cursor-pointer"
: "bg-[var(--border)] text-[var(--muted)] cursor-not-allowed"
)}
onClick={(e) => {
@@ -136,7 +136,7 @@ export default function DownloadResult({ result, onReset, soundOnCompletion, onT
target="_blank"
rel="noopener noreferrer"
aria-label="Preview video in new tab"
- className="flex items-center justify-center gap-2 px-4 py-3 border border-[var(--border)] text-[var(--muted)] text-sm rounded-lg hover:bg-[var(--bg)] transition-colors"
+ className="flex items-center justify-center gap-2 px-4 py-3 border border-[var(--border)] text-[var(--muted)] text-sm rounded-lg hover:border-[var(--accent)] hover:bg-[var(--accent-muted)] hover:text-[var(--text)] transition-colors"
>
Preview
@@ -145,7 +145,7 @@ export default function DownloadResult({ result, onReset, soundOnCompletion, onT
title="Reset and upload a new video"
aria-label="Upload a new video"
onClick={handleReset}
- className="flex items-center gap-2 px-4 py-3 border border-[var(--border)] text-[var(--muted)] text-sm rounded-lg hover:bg-[var(--bg)] transition-colors"
+ className="flex items-center gap-2 px-4 py-3 border border-[var(--border)] text-[var(--muted)] text-sm rounded-lg hover:border-[var(--accent)] hover:bg-[var(--accent-muted)] hover:text-[var(--text)] transition-colors"
>
New
@@ -155,7 +155,7 @@ export default function DownloadResult({ result, onReset, soundOnCompletion, onT
target="_blank"
rel="noopener noreferrer"
aria-label="Share on X (opens in a new tab)"
- className="flex-1 min-w-[10rem] flex items-center justify-center gap-2 py-3 border border-[var(--border)] text-[var(--text)] text-sm font-heading font-bold uppercase tracking-wide rounded-lg hover:bg-[var(--bg)] transition-colors"
+ className="flex-1 min-w-[10rem] flex items-center justify-center gap-2 py-3 border border-[var(--border)] text-[var(--text)] text-sm font-heading font-bold uppercase tracking-wide rounded-lg hover:border-[var(--accent)] hover:bg-[var(--accent-muted)] transition-colors"
>
Share on X
diff --git a/src/components/ExportSettings.tsx b/src/components/ExportSettings.tsx
index 8b7f5286..6eee9b9b 100644
--- a/src/components/ExportSettings.tsx
+++ b/src/components/ExportSettings.tsx
@@ -112,7 +112,7 @@ export default function ExportSettings({
{isGif && (
-
+
⚠ GIF files can be very large. Keep clips under 10 s for best results.
)}
@@ -176,7 +176,7 @@ export default function ExportSettings({
className={cn(
"text-xs",
recipe.stabilization
- ? "text-red-700 font-medium"
+ ? "text-[var(--error)] font-medium"
: "text-[var(--muted)]"
)}
>
diff --git a/src/components/FileUpload.tsx b/src/components/FileUpload.tsx
index d76845c5..f9f2127e 100644
--- a/src/components/FileUpload.tsx
+++ b/src/components/FileUpload.tsx
@@ -127,7 +127,7 @@ export default function FileUpload({
// ── File info (shown after upload) ───────────────────
const FileInfo = () => (
-
+
@@ -136,18 +136,18 @@ export default function FileUpload({
-
+
{currentFile?.name}
{currentFile && (
-
+
{currentFile.name.includes(".")
? currentFile.name.split(".").pop()
: "VIDEO"}
)}
-
+
{formatBytes(currentFile?.size ?? 0)}
{duration > 0
@@ -168,12 +168,12 @@ export default function FileUpload({
-
+
Supports: MP4, MOV, AVI, MKV, WebM, and most video formats
{fileError && (
-
{fileError}
+
{fileError}
)}
@@ -239,12 +239,12 @@ export default function FileUpload({
MP4 / MOV / AVI / WebM
-
+
Supports: MP4, MOV, AVI, MKV, WebM, and most video formats up to 2GB
{fileError && (
-
{fileError}
+
{fileError}
)}
{error && (
-
+
{error}
)}
{warning && (
-
+
{warning}
)}
@@ -309,4 +309,4 @@ export default function FileUpload({
>
);
-}
\ No newline at end of file
+}
diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx
index d055027c..c6006f0d 100644
--- a/src/components/Footer.tsx
+++ b/src/components/Footer.tsx
@@ -41,7 +41,7 @@ export default function Footer() {
].map((tag) => (
{tag.icon} {tag.label}
@@ -59,19 +59,19 @@ export default function Footer() {
href="https://github.com/magic-peach/reframe"
target="_blank"
rel="noopener"
- className="opacity-70 hover:opacity-100 hover:text-red-400 hover:scale-110 transition-all duration-500 ease-in-out w-fit flex items-center gap-2 group"
+ className="opacity-70 hover:opacity-100 hover:text-[var(--accent)] hover:scale-110 transition-all duration-500 ease-in-out w-fit flex items-center gap-2 group"
>
GitHub
Contact
Privacy Policy
@@ -102,7 +102,7 @@ export default function Footer() {
) : (
diff --git a/src/components/ImageOverlay.tsx b/src/components/ImageOverlay.tsx
index 1f74359b..9af09391 100644
--- a/src/components/ImageOverlay.tsx
+++ b/src/components/ImageOverlay.tsx
@@ -59,7 +59,7 @@ export default function ImageOverlayPanel({
const isSolidOpacity = overlayOpacity > 75;
return (
-
+
{/* Side-by-Side Area */}
@@ -67,8 +67,8 @@ export default function ImageOverlayPanel({
{/* Left Side: Dynamic Upload / Preview Square */}
{thumbUrl ? (
{/* Right Side: Horizontal Details Card */}
-
+
{overlayFile ? (
<>
{/* File Info block */}
@@ -116,7 +116,7 @@ export default function ImageOverlayPanel({
type="button"
onClick={() => setOverlayFile(null)}
aria-label="Remove overlay image"
- className="w-6 h-6 rounded flex items-center justify-center bg-red-500/10 hover:bg-red-500/20 text-red-400 border border-red-500/20 transition shrink-0"
+ className="w-6 h-6 rounded flex items-center justify-center bg-[var(--error-bg)] hover:bg-[var(--error-hover)] text-[var(--error)] border border-[var(--error-border)] transition shrink-0"
>
@@ -143,11 +143,11 @@ export default function ImageOverlayPanel({
onClick={() => setOverlayPosition(value)}
className={`rounded border py-0.5 text-center text-[10px] transition flex items-center justify-center gap-0.5 ${
overlayPosition === value
- ? "border-film-500 text-white bg-film-600/10"
- : "border-[var(--border)] text-[var(--muted)] hover:bg-white/5"
+ ? "border-[var(--accent)] text-[var(--text)] bg-[var(--accent-muted)]"
+ : "border-[var(--border)] text-[var(--muted)] hover:bg-[var(--accent-muted)]"
}`}
>
-
+
{icon}
{label}
@@ -165,8 +165,8 @@ export default function ImageOverlayPanel({
onClick={() => setOverlaySize(100)}
className={`rounded border py-0.5 text-center text-[10px] transition ${
isSmallSize
- ? "border-film-500 text-white bg-film-600/10"
- : "border-[var(--border)] text-[var(--muted)] hover:bg-white/5"
+ ? "border-[var(--accent)] text-[var(--text)] bg-[var(--accent-muted)]"
+ : "border-[var(--border)] text-[var(--muted)] hover:bg-[var(--accent-muted)]"
}`}
>
Small
@@ -176,8 +176,8 @@ export default function ImageOverlayPanel({
onClick={() => setOverlaySize(250)}
className={`rounded border py-0.5 text-center text-[10px] transition ${
isMediumSize
- ? "border-film-500 text-white bg-film-600/10"
- : "border-[var(--border)] text-[var(--muted)] hover:bg-white/5"
+ ? "border-[var(--accent)] text-[var(--text)] bg-[var(--accent-muted)]"
+ : "border-[var(--border)] text-[var(--muted)] hover:bg-[var(--accent-muted)]"
}`}
>
Medium
@@ -187,8 +187,8 @@ export default function ImageOverlayPanel({
onClick={() => setOverlaySize(450)}
className={`rounded border py-0.5 text-center text-[10px] transition ${
isLargeSize
- ? "border-film-500 text-white bg-film-600/10"
- : "border-[var(--border)] text-[var(--muted)] hover:bg-white/5"
+ ? "border-[var(--accent)] text-[var(--text)] bg-[var(--accent-muted)]"
+ : "border-[var(--border)] text-[var(--muted)] hover:bg-[var(--accent-muted)]"
}`}
>
Large
@@ -205,8 +205,8 @@ export default function ImageOverlayPanel({
onClick={() => setOverlayOpacity(25)}
className={`rounded border py-0.5 text-center text-[10px] transition ${
isFaintOpacity
- ? "border-film-500 text-white bg-film-600/10"
- : "border-[var(--border)] text-[var(--muted)] hover:bg-white/5"
+ ? "border-[var(--accent)] text-[var(--text)] bg-[var(--accent-muted)]"
+ : "border-[var(--border)] text-[var(--muted)] hover:bg-[var(--accent-muted)]"
}`}
>
25%
@@ -216,8 +216,8 @@ export default function ImageOverlayPanel({
onClick={() => setOverlayOpacity(60)}
className={`rounded border py-0.5 text-center text-[10px] transition ${
isMediumOpacity
- ? "border-film-500 text-white bg-film-600/10"
- : "border-[var(--border)] text-[var(--muted)] hover:bg-white/5"
+ ? "border-[var(--accent)] text-[var(--text)] bg-[var(--accent-muted)]"
+ : "border-[var(--border)] text-[var(--muted)] hover:bg-[var(--accent-muted)]"
}`}
>
60%
@@ -227,8 +227,8 @@ export default function ImageOverlayPanel({
onClick={() => setOverlayOpacity(100)}
className={`rounded border py-0.5 text-center text-[10px] transition ${
isSolidOpacity
- ? "border-film-500 text-white bg-film-600/10"
- : "border-[var(--border)] text-[var(--muted)] hover:bg-white/5"
+ ? "border-[var(--accent)] text-[var(--text)] bg-[var(--accent-muted)]"
+ : "border-[var(--border)] text-[var(--muted)] hover:bg-[var(--accent-muted)]"
}`}
>
100%
@@ -239,4 +239,4 @@ export default function ImageOverlayPanel({
)}
);
-}
\ No newline at end of file
+}
diff --git a/src/components/ScrollToTop.tsx b/src/components/ScrollToTop.tsx
index 57bc329c..e2706342 100644
--- a/src/components/ScrollToTop.tsx
+++ b/src/components/ScrollToTop.tsx
@@ -34,7 +34,7 @@ export default function ScrollToTop() {
className="
fixed bottom-5 right-5 z-50
h-12 w-12 rounded-full
- bg-black text-white shadow-lg
+ bg-[var(--accent)] text-white shadow-[var(--shadow)]
flex items-center justify-center
md:hidden
"
@@ -42,4 +42,4 @@ export default function ScrollToTop() {
↑
);
-}
\ No newline at end of file
+}
diff --git a/src/components/ThemeToggle.tsx b/src/components/ThemeToggle.tsx
index b94b2b13..03e32bef 100644
--- a/src/components/ThemeToggle.tsx
+++ b/src/components/ThemeToggle.tsx
@@ -17,9 +17,10 @@ export function ThemeToggle() {
bg-[var(--surface)]
text-[var(--text)]
border border-[var(--border)]
- hover:opacity-90
- focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
- transition-colors duration-200
+ hover:border-[var(--accent)] hover:bg-[var(--accent-muted)]
+ focus:outline-none focus:ring-2 focus:ring-[var(--accent)] focus:ring-offset-2
+ focus:ring-offset-[var(--bg)]
+ transition-all duration-200
"
>
{isDark ? (
diff --git a/src/components/ThumbnailStrip.tsx b/src/components/ThumbnailStrip.tsx
index 22c1b5e7..f5094722 100644
--- a/src/components/ThumbnailStrip.tsx
+++ b/src/components/ThumbnailStrip.tsx
@@ -43,6 +43,10 @@ export default function ThumbnailStrip({
objectUrlsRef.current = [];
}, []);
+ const cancelThumbnailRun = useCallback(() => {
+ lastRunIdRef.current += 1;
+ }, []);
+
const generateThumbnails = useCallback(async () => {
if (!videoSrc || duration <= 0) return;
@@ -144,11 +148,10 @@ export default function ThumbnailStrip({
generateThumbnails();
}
return () => {
- // Increment ref to invalidate any pending async operations
- lastRunIdRef.current++;
+ cancelThumbnailRun();
revokeAllObjectUrls();
};
- }, [generateThumbnails, revokeAllObjectUrls, videoSrc, duration]);
+ }, [cancelThumbnailRun, generateThumbnails, revokeAllObjectUrls, videoSrc, duration]);
const formatTime = (seconds: number) => {
const m = Math.floor(seconds / 60);
@@ -239,11 +242,12 @@ export default function ThumbnailStrip({
);
-}
\ No newline at end of file
+}
diff --git a/src/components/TrimControl.tsx b/src/components/TrimControl.tsx
index 577dc1fa..aa064cd2 100644
--- a/src/components/TrimControl.tsx
+++ b/src/components/TrimControl.tsx
@@ -256,7 +256,9 @@ export default function TrimControl({ recipe, onChange, duration, file }: Props)
aria-label="Trim start time in seconds"
aria-invalid={invalidStart}
aria-describedby={invalidStart ? "trim-start-error" : undefined}
- className={`${inputClass} ${invalidStart ? "border-red-500 focus:ring-red-400" : "border-[var(--border)]"}`}
+ className={`${inputClass} ${
+ invalidStart ? "border-[var(--error)]" : "border-[var(--border)]"
+ }`}
placeholder="0"
/>
{invalidStart && (
@@ -290,7 +292,9 @@ export default function TrimControl({ recipe, onChange, duration, file }: Props)
aria-label="Trim end time in seconds"
aria-invalid={invalidEnd}
aria-describedby={invalidEnd ? "trim-end-error" : undefined}
- className={`${inputClass} ${invalidEnd ? "border-red-500 focus:ring-red-400" : "border-[var(--border)]"}`}
+ className={`${inputClass} ${
+ invalidEnd ? "border-[var(--error)]" : "border-[var(--border)]"
+ }`}
placeholder={duration > 0 ? `${duration.toFixed(1)}` : "full length"}
/>
{invalidEnd && (
@@ -313,7 +317,7 @@ export default function TrimControl({ recipe, onChange, duration, file }: Props)
)}
{recipe.trimEnd !== null &&
recipe.trimEnd - recipe.trimStart < MIN_CLIP_DURATION && (
-
+
Clip must be at least 0.1 seconds long.
)}
diff --git a/src/components/VideoEditor.tsx b/src/components/VideoEditor.tsx
index 0fb4429e..d971fc7b 100644
--- a/src/components/VideoEditor.tsx
+++ b/src/components/VideoEditor.tsx
@@ -331,7 +331,7 @@ export default function VideoEditor() {
paddingTop: 'clamp(0.5rem,2vw,0.75rem)',
}}
>
-
+
No login. No ads. 100% private.
@@ -339,7 +339,7 @@ export default function VideoEditor() {
className="flex flex-wrap justify-center text-center items-center gap-2 text-sm font-heading font-semibold uppercase tracking-widest text-[var(--muted)] pb-1"
style={{ justifyContent: 'center', textAlign: 'center', margin: '0', width: 'auto' }}
>
-
+
No login. No ads. 100% private - your video never leaves your device.
@@ -605,7 +605,7 @@ export default function VideoEditor() {
(isProcessing || !file) && "pointer-events-none opacity-50"
)}>
{!file && (
-
+
Getting Started
@@ -669,7 +669,7 @@ export default function VideoEditor() {
"w-full flex items-center justify-center gap-3 py-5 min-h-[44px] rounded-xl",
"font-display text-2xl tracking-widest transition-all duration-200",
file && !isProcessing
- ? "bg-film-600 hover:bg-film-700 hover:scale-[1.01] text-white shadow-lg shadow-film-200 active:scale-[0.98] cursor-pointer"
+ ? "bg-[var(--accent)] hover:bg-[var(--accent-hover)] hover:scale-[1.02] text-white shadow-[var(--shadow)] active:scale-[0.98] cursor-pointer"
: "bg-[var(--border)] text-[var(--muted)] cursor-not-allowed"
)}
>
@@ -687,4 +687,4 @@ export default function VideoEditor() {
);
-}
\ No newline at end of file
+}
diff --git a/src/components/VideoPreview.tsx b/src/components/VideoPreview.tsx
index 0f20882c..d684c1e5 100644
--- a/src/components/VideoPreview.tsx
+++ b/src/components/VideoPreview.tsx
@@ -217,14 +217,14 @@ export default function VideoPreview({
{isLoading && (
)}
@@ -246,18 +246,18 @@ export default function VideoPreview({
{overlay.mode === "fit" ? (
// Letterbox: semi-transparent bars outside the content area
<>
-
-
-
-
+
+
+
+
>
) : (
// Fill/crop: dashed border around the surviving area, dimmed outside
<>
-
-
-
-
+
+
+
+
setShowOverlay((v) => !v)}
className={`absolute top-2 left-2 px-2 py-1 text-[10px] font-heading font-bold uppercase tracking-wider rounded transition-colors z-10 pointer-events-auto ${
showOverlay
- ? "bg-film-600 text-white"
- : "bg-black/60 text-white/70 hover:bg-black/80"
+ ? "bg-[var(--accent)] text-white"
+ : "bg-[var(--surface)] text-[var(--muted)] hover:bg-[var(--accent-muted)] hover:text-[var(--text)]"
}`}
aria-pressed={showOverlay}
aria-label={showOverlay ? "Hide framing overlay" : "Show framing overlay"}
@@ -309,8 +309,8 @@ export default function VideoPreview({
onClick={() => setShowComparison((v) => !v)}
className={`absolute top-2 right-32 px-2 py-1 text-[10px] font-heading font-bold uppercase tracking-wider rounded transition-colors z-10 pointer-events-auto ${
showComparison
- ? "bg-film-600 text-white"
- : "bg-black/60 text-white/70 hover:bg-black/80"
+ ? "bg-[var(--accent)] text-white"
+ : "bg-[var(--surface)] text-[var(--muted)] hover:bg-[var(--accent-muted)] hover:text-[var(--text)]"
}`}
aria-pressed={showComparison}
aria-label={showComparison ? "Hide comparison preview" : "Show comparison preview"}
@@ -325,7 +325,7 @@ export default function VideoPreview({
diff --git a/src/components/WaveformCanvas.tsx b/src/components/WaveformCanvas.tsx
index cff6a6bc..ee75055c 100644
--- a/src/components/WaveformCanvas.tsx
+++ b/src/components/WaveformCanvas.tsx
@@ -35,7 +35,7 @@ export default function WaveformCanvas({ samples, loading, hasAudio }: Props) {
const midY = height / 2;
// Read theme colour from :root — falls back to a visible purple
- const accentColor = getCssVar("--film-500", "#8b5cf6");
+ const accentColor = getCssVar("--accent", "#4f6ef7");
if (!hasAudio || samples.length === 0) {
// Flat centre line for silent videos
@@ -92,4 +92,4 @@ export default function WaveformCanvas({ samples, loading, hasAudio }: Props) {
className="w-full h-16 rounded-md border border-[var(--border)] bg-[var(--surface)]"
/>
);
-}
\ No newline at end of file
+}
diff --git a/src/components/components.tsx b/src/components/components.tsx
index b4994015..bd5241a5 100644
--- a/src/components/components.tsx
+++ b/src/components/components.tsx
@@ -14,10 +14,7 @@ export function Card({ title, description, children, className = "" }: CardProps
return (
@@ -46,21 +43,21 @@ export function Button({ variant = "primary", className = "", children, ...props
primary: `
bg-[var(--accent)]
text-white
- hover:opacity-90
- focus:ring-blue-500
+ hover:bg-[var(--accent-hover)] hover:scale-[1.02]
+ focus:ring-[var(--accent)]
`,
secondary: `
- bg-[var(--surface)]
+ bg-[var(--accent-muted)]
text-[var(--text)]
- border border-[var(--border)]
- hover:opacity-90
- focus:ring-gray-400
+ border border-[var(--accent)]
+ hover:bg-[var(--surface)] hover:scale-[1.02]
+ focus:ring-[var(--accent)]
`,
ghost: `
bg-transparent
- text-[var(--text)]
- hover:bg-[var(--surface)]
- focus:ring-gray-400
+ text-[var(--muted)]
+ hover:bg-[var(--accent-muted)] hover:text-[var(--text)]
+ focus:ring-[var(--accent)]
`,
};
@@ -68,9 +65,10 @@ export function Button({ variant = "primary", className = "", children, ...props
({
+ matches: false,
+ media: query,
+ onchange: null,
+ addListener: () => {},
+ removeListener: () => {},
+ addEventListener: () => {},
+ removeEventListener: () => {},
+ dispatchEvent: () => false,
+ }),
+})