Consolidate CSS architecture and eliminate repetition
All checks were successful
Lint and Build / build (pull_request) Successful in 2m43s
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:
parent
3bad71cb26
commit
4f9e6d2097
5 changed files with 77 additions and 81 deletions
|
@ -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;
|
||||
|
|
42
app/page.tsx
42
app/page.tsx
|
@ -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>
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
.linkButton {
|
||||
padding: 10px 20px;
|
||||
background: #0070f3;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
border-radius: 5px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue