Consolidate CSS architecture and eliminate repetition
All checks were successful
Lint and Build / build (pull_request) Successful in 2m43s

- Add CSS custom properties for commonly used gradients
- Consolidate duplicate button variants (.btn.active and .btn-success)
- Replace inline gradient styles with semantic CSS classes
- Standardize spacing with utility classes (mr-1, mr-2, mr-4, ml-3)
- Remove unused Home.module.css file
- Replace hard-coded colors with Solarized CSS variables
- Add scene-specific button classes (btn-scene-preview, btn-scene-transition)

Reduces CSS duplication, improves maintainability, and ensures consistent
styling throughout the application with reusable utility classes.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Decobus 2025-07-25 21:39:49 -04:00
parent 3bad71cb26
commit 4f9e6d2097
5 changed files with 77 additions and 81 deletions

View file

@ -20,6 +20,14 @@
--solarized-red: #dc322f;
--solarized-magenta: #d33682;
--solarized-violet: #6c71c4;
/* Gradient Custom Properties */
--gradient-primary: linear-gradient(135deg, var(--solarized-blue), var(--solarized-cyan));
--gradient-active: linear-gradient(135deg, var(--solarized-green), var(--solarized-yellow));
--gradient-danger: linear-gradient(135deg, var(--solarized-red), var(--solarized-orange));
--gradient-preview: linear-gradient(135deg, var(--solarized-yellow), var(--solarized-orange));
--gradient-transition: linear-gradient(135deg, var(--solarized-red), var(--solarized-magenta));
--gradient-body: linear-gradient(135deg, #002b36 0%, #073642 50%, #002b36 100%);
}
/* Modern CSS Foundation */
@ -34,8 +42,8 @@ html {
}
body {
background: linear-gradient(135deg, #002b36 0%, #073642 50%, #002b36 100%);
color: #93a1a1;
background: var(--gradient-body);
color: var(--solarized-base1);
min-height: 100vh;
line-height: 1.6;
}
@ -75,8 +83,8 @@ body {
/* Modern Button System */
.btn {
background: linear-gradient(135deg, #268bd2, #2aa198);
color: #fdf6e3;
background: var(--gradient-primary);
color: var(--solarized-base3);
border: none;
padding: 12px 24px;
border-radius: 12px;
@ -93,9 +101,10 @@ body {
text-decoration: none;
}
.btn.active {
background: linear-gradient(135deg, #859900, #b58900);
color: #fdf6e3;
.btn.active,
.btn-success {
background: var(--gradient-active);
color: var(--solarized-base3);
box-shadow: 0 0 0 3px rgba(133, 153, 0, 0.5);
transform: translateY(-1px);
font-weight: 700;
@ -117,7 +126,7 @@ body {
.btn-secondary {
background: rgba(88, 110, 117, 0.3);
border: 1px solid rgba(131, 148, 150, 0.4);
color: #93a1a1;
color: var(--solarized-base1);
backdrop-filter: blur(10px);
}
@ -127,25 +136,14 @@ body {
box-shadow: 0 6px 20px rgba(88, 110, 117, 0.3);
}
/* Success Button */
.btn-success {
background: linear-gradient(135deg, #859900, #b58900);
color: #fdf6e3;
}
.btn-success:hover {
background: linear-gradient(135deg, #b58900, #859900);
box-shadow: 0 6px 20px rgba(133, 153, 0, 0.4);
}
/* Danger Button */
.btn-danger {
background: linear-gradient(135deg, #dc322f, #cb4b16);
color: #fdf6e3;
background: var(--gradient-danger);
color: var(--solarized-base3);
}
.btn-danger:hover {
background: linear-gradient(135deg, #cb4b16, #dc322f);
background: linear-gradient(135deg, var(--solarized-orange), var(--solarized-red));
box-shadow: 0 6px 20px rgba(220, 50, 47, 0.4);
}
@ -169,6 +167,18 @@ body {
flex-shrink: 0;
}
/* Scene Button Variants */
.btn-scene-preview {
background: var(--gradient-preview);
color: var(--solarized-base3);
}
.btn-scene-transition {
background: var(--gradient-transition);
color: var(--solarized-base3);
min-width: 120px;
}
/* Form spacing fixes since Tailwind gap classes aren't working */
.form-row {
display: flex;
@ -194,7 +204,7 @@ body {
border: 1px solid rgba(88, 110, 117, 0.4);
border-radius: 12px;
padding: 12px 16px;
color: #93a1a1;
color: var(--solarized-base1);
width: 100%;
transition: all 0.3s ease;
}
@ -205,7 +215,7 @@ body {
.input:focus {
outline: none;
border-color: #268bd2;
border-color: var(--solarized-blue);
box-shadow: 0 0 0 3px rgba(38, 139, 210, 0.2);
}
@ -215,7 +225,7 @@ body {
border: 1px solid rgba(88, 110, 117, 0.4);
border-radius: 12px;
padding: 12px 16px;
color: #93a1a1;
color: var(--solarized-base1);
width: 100%;
text-align: left;
cursor: pointer;
@ -270,7 +280,7 @@ body {
cursor: pointer;
transition: all 0.2s ease;
border-bottom: 1px solid rgba(88, 110, 117, 0.2);
color: #93a1a1;
color: var(--solarized-base1);
}
.dropdown-item:last-child {
@ -283,7 +293,7 @@ body {
.dropdown-item.active {
background: rgba(38, 139, 210, 0.3);
color: #fdf6e3;
color: var(--solarized-base3);
}
/* Icon Sizes */
@ -378,6 +388,22 @@ body {
margin-bottom: 32px;
}
.ml-3 {
margin-left: 12px;
}
.mr-1 {
margin-right: 4px;
}
.mr-2 {
margin-right: 8px;
}
.mr-4 {
margin-right: 16px;
}
.p-4 {
padding: 16px;
}
@ -420,7 +446,7 @@ body {
.collapsible-icon {
flex-shrink: 0;
transition: transform 0.3s ease;
color: #93a1a1;
color: var(--solarized-base1);
}
.collapsible-icon.open {
@ -431,14 +457,14 @@ body {
flex: 1;
font-size: 18px;
font-weight: 600;
color: #fdf6e3;
color: var(--solarized-base3);
text-align: left;
margin: 0;
}
.collapsible-count {
background: rgba(38, 139, 210, 0.2);
color: #268bd2;
color: var(--solarized-blue);
padding: 4px 12px;
border-radius: 20px;
font-size: 14px;

View file

@ -193,28 +193,28 @@ export default function Home() {
return {
isActive: true,
text: `Program & Preview: ${sceneName}`,
background: 'linear-gradient(135deg, var(--solarized-green), var(--solarized-yellow))',
className: 'active',
showTransition: false
};
} else if (isProgram) {
return {
isActive: true,
text: `Program: ${sceneName}`,
background: 'linear-gradient(135deg, var(--solarized-green), var(--solarized-yellow))',
className: 'active',
showTransition: false
};
} else if (isPreview) {
return {
isActive: true,
text: `Preview: ${sceneName}`,
background: 'linear-gradient(135deg, var(--solarized-yellow), var(--solarized-orange))',
className: 'btn-scene-preview',
showTransition: true
};
} else {
return {
isActive: false,
text: `Set Preview: ${sceneName}`,
background: 'linear-gradient(135deg, var(--solarized-blue), var(--solarized-cyan))',
className: '',
showTransition: false
};
}
@ -224,14 +224,14 @@ export default function Home() {
return {
isActive: true,
text: `Active: ${sceneName}`,
background: 'linear-gradient(135deg, var(--solarized-green), var(--solarized-yellow))',
className: 'active',
showTransition: false
};
} else {
return {
isActive: false,
text: `Switch to ${sceneName}`,
background: 'linear-gradient(135deg, var(--solarized-blue), var(--solarized-cyan))',
className: '',
showTransition: false
};
}
@ -289,20 +289,14 @@ export default function Home() {
<>
<button
onClick={() => handleSceneSwitch('1-Screen')}
className={`btn ${buttonState.isActive ? 'active' : ''}`}
style={{ background: buttonState.background }}
className={`btn ${buttonState.className}`}
>
{buttonState.text}
</button>
{buttonState.showTransition && (
<button
onClick={handleTransition}
className="btn"
style={{
background: 'linear-gradient(135deg, var(--solarized-red), var(--solarized-magenta))',
minWidth: '120px',
marginLeft: '12px'
}}
className="btn btn-scene-transition ml-3"
>
Go Live
</button>
@ -335,20 +329,14 @@ export default function Home() {
<>
<button
onClick={() => handleSceneSwitch('2-Screen')}
className={`btn ${buttonState.isActive ? 'active' : ''}`}
style={{ background: buttonState.background }}
className={`btn ${buttonState.className}`}
>
{buttonState.text}
</button>
{buttonState.showTransition && (
<button
onClick={handleTransition}
className="btn"
style={{
background: 'linear-gradient(135deg, var(--solarized-red), var(--solarized-magenta))',
minWidth: '120px',
marginLeft: '12px'
}}
className="btn btn-scene-transition ml-3"
>
Go Live
</button>
@ -395,20 +383,14 @@ export default function Home() {
<>
<button
onClick={() => handleSceneSwitch('4-Screen')}
className={`btn ${buttonState.isActive ? 'active' : ''}`}
style={{ background: buttonState.background }}
className={`btn ${buttonState.className}`}
>
{buttonState.text}
</button>
{buttonState.showTransition && (
<button
onClick={handleTransition}
className="btn"
style={{
background: 'linear-gradient(135deg, var(--solarized-red), var(--solarized-magenta))',
minWidth: '120px',
marginLeft: '12px'
}}
className="btn btn-scene-transition ml-3"
>
Go Live
</button>

View file

@ -131,12 +131,11 @@ function StreamsByTeam({ streams, teams, onDelete }: StreamsByTeamProps) {
<div className="flex items-center justify-between">
<div className="flex items-center">
<div
className="bg-gradient-to-br from-green-500 to-blue-600 rounded-lg flex items-center justify-center text-white font-bold flex-shrink-0"
className="bg-gradient-to-br from-green-500 to-blue-600 rounded-lg flex items-center justify-center text-white font-bold flex-shrink-0 mr-4"
style={{
width: '64px',
height: '64px',
fontSize: '24px',
marginRight: '16px'
fontSize: '24px'
}}
>
{stream.name.charAt(0).toUpperCase()}
@ -153,8 +152,7 @@ function StreamsByTeam({ streams, teams, onDelete }: StreamsByTeamProps) {
href={stream.url}
target="_blank"
rel="noopener noreferrer"
className="btn btn-primary text-sm"
style={{ marginRight: '8px' }}
className="btn btn-primary text-sm mr-2"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />

View file

@ -86,7 +86,7 @@ export default function Footer() {
<div className="mb-4">
<h3 className="font-semibold mb-2">OBS Studio</h3>
<div className="flex items-center">
<div className={`status-dot ${obsStatus?.connected ? 'connected' : 'disconnected'}`} style={{marginRight: '4px'}}></div>
<div className={`status-dot ${obsStatus?.connected ? 'connected' : 'disconnected'} mr-1`}></div>
<p className="text-sm opacity-60">
{obsStatus?.connected ? 'Connected' : 'Disconnected'}
</p>
@ -102,17 +102,17 @@ export default function Footer() {
{obsStatus.connected && (
<div className="flex flex-wrap gap-6 mt-4">
<div className={`flex items-center ${obsStatus.streaming ? 'text-red-400' : 'opacity-60'}`}>
<div className={`status-dot ${obsStatus.streaming ? 'streaming' : 'idle'}`} style={{width: '10px', height: '10px', marginRight: '4px'}}></div>
<span className="text-sm font-medium" style={{marginRight: '8px'}}>{obsStatus.streaming ? 'LIVE' : 'OFFLINE'}</span>
<div className={`status-dot ${obsStatus.streaming ? 'streaming' : 'idle'} mr-1`} style={{width: '10px', height: '10px'}}></div>
<span className="text-sm font-medium mr-2">{obsStatus.streaming ? 'LIVE' : 'OFFLINE'}</span>
</div>
<div className={`flex items-center ${obsStatus.recording ? 'text-red-400' : 'opacity-60'}`}>
<div className={`status-dot ${obsStatus.recording ? 'streaming' : 'idle'}`} style={{width: '10px', height: '10px', marginRight: '4px'}}></div>
<span className="text-sm font-medium" style={{marginRight: '8px'}}>{obsStatus.recording ? 'REC' : 'IDLE'}</span>
<div className={`status-dot ${obsStatus.recording ? 'streaming' : 'idle'} mr-1`} style={{width: '10px', height: '10px'}}></div>
<span className="text-sm font-medium mr-2">{obsStatus.recording ? 'REC' : 'IDLE'}</span>
</div>
<div className={`flex items-center ${obsStatus.studioModeEnabled ? 'text-yellow-400' : 'opacity-60'}`}>
<div className={`status-dot ${obsStatus.studioModeEnabled ? 'connected' : 'idle'}`} style={{width: '10px', height: '10px', marginRight: '4px'}}></div>
<div className={`status-dot ${obsStatus.studioModeEnabled ? 'connected' : 'idle'} mr-1`} style={{width: '10px', height: '10px'}}></div>
<span className="text-sm font-medium">{obsStatus.studioModeEnabled ? 'STUDIO' : 'DIRECT'}</span>
</div>
</div>

View file

@ -1,10 +0,0 @@
.linkButton {
padding: 10px 20px;
background: #0070f3;
color: #fff;
text-decoration: none;
border-radius: 5px;
display: inline-block;
text-align: center;
}