JavaScript and React course Β· Module 7: Styling and Animations
Project: Cosmic Mission Control Panel
In this lesson4
Congratulations, Navigator! You have reached the end of this module and it is time to combine all the styling techniques you have learned into one comprehensive project. Your task is to create a Cosmic Mission Control Panel - an interactive dashboard that utilizes knowledge from the entire module.
Project Goal
You will build a spaceship control panel that combines:
- Styled Components with dynamic theming
- CSS Animations with
@keyframes - Responsive layout with CSS Grid and Flexbox
- Theming (light/dark mode)
- Interactive elements with hover effects and transitions
Project Architecture
The project consists of several key sections:
1. Theme System (Theming)
We use ThemeProvider from styled-components to manage colors and styles across the entire application:
1const darkTheme = {
2 bg: '#0a0a1a',
3 surface: '#12122a',
4 accent: '#00ff88',
5 text: '#e0e0e0',
6 danger: '#ff4444',
7 warning: '#ffaa00'
8};
9
10const lightTheme = {
11 bg: '#f0f4f8',
12 surface: '#ffffff',
13 accent: '#0066ff',
14 text: '#1a1a2e',
15 danger: '#cc0000',
16 warning: '#cc8800'
17};2. CSS Animations
The panel uses several types of animations you learned in the module:
1/* Status pulsing */
2@keyframes pulse {
3 0%, 100% { opacity: 1; }
4 50% { opacity: 0.5; }
5}
6
7/* Slide in with offset */
8@keyframes slideIn {
9 from {
10 opacity: 0;
11 transform: translateY(20px);
12 }
13 to {
14 opacity: 1;
15 transform: translateY(0);
16 }
17}
18
19/* Radar rotation */
20@keyframes rotate {
21 from { transform: rotate(0deg); }
22 to { transform: rotate(360deg); }
23}3. Styled Components with Props
Dynamic component styling based on passed props:
1const StatusBadge = styled.span`
2 padding: 4px 12px;
3 border-radius: 12px;
4 font-size: 12px;
5 font-weight: bold;
6 background: ${props => props.active ? '#00ff8833' : '#ff444433'};
7 color: ${props => props.active ? '#00ff88' : '#ff4444'};
8 animation: pulse 2s infinite;
9`;
10
11const Card = styled.div`
12 background: ${props => props.theme.surface};
13 border: 1px solid ${props => props.theme.accent}33;
14 border-radius: 12px;
15 padding: 20px;
16 transition: transform 0.3s ease, box-shadow 0.3s ease;
17
18 &:hover {
19 transform: translateY(-4px);
20 box-shadow: 0 8px 25px rgba(0, 255, 136, 0.15);
21 }
22`;4. Responsive Layout with CSS Grid
The panel automatically adapts to the screen width:
1.dashboard-grid {
2 display: grid;
3 grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
4 gap: 20px;
5 padding: 20px;
6}
7
8@media (max-width: 768px) {
9 .dashboard-grid {
10 grid-template-columns: 1fr;
11 }
12}5. Complete Component Structure
The main App component brings all elements together:
1function App() {
2 const [isDark, setIsDark] = useState(true);
3
4 return (
5 <ThemeProvider theme={isDark ? darkTheme : lightTheme}>
6 <GlobalStyle />
7 <Header>
8 <Title>Mission Control</Title>
9 <ThemeToggle onClick={() => setIsDark(!isDark)}>
10 {isDark ? 'Light' : 'Dark'}
11 </ThemeToggle>
12 </Header>
13 <DashboardGrid>
14 <StatusCard systems={systemsData} />
15 <RadarCard />
16 <MissionsCard missions={missionsData} />
17 <CrewCard crew={crewData} />
18 </DashboardGrid>
19 </ThemeProvider>
20 );
21}Key Techniques Used in the Project
| Technique | Usage in Project |
|---|---|
| Styled Components | All UI components |
| ThemeProvider | Switching dark/light mode |
@keyframes | Status pulsing, radar rotation, card entry |
| CSS Grid | Dashboard layout |
| Flexbox | Internal card layout |
transition | Hover effects on cards and buttons |
| Dynamic props | Status colors, conditional styles |
| Responsiveness | auto-fit and minmax in grid |
Module Summary
In this module, you learned the full spectrum of styling techniques in React:
- Classic CSS and CSS Modules - the foundation of styling with class isolation
- Styled Components - CSS-in-JS with dynamic props and theming
- SCSS - preprocessor with variables, mixins, and nesting
- Tailwind CSS - utility-first approach for fast prototyping
- UI Frameworks - ready component libraries (MUI, Ant Design, Chakra)
- Design Systems - building consistent design systems
- React Transition Group - animating component lifecycle
- CSS Animations -
@keyframes,transition, and advanced effects
Each of these techniques has its place in the arsenal of a cosmic programmer. The best engineers know how to combine them into a coherent system - exactly as in this project.
Try the panel below and experiment with the code - change the theme colors, add new animations, or expand the dashboard sections!
Code for this lesson: App.jsx
1import React, { useState } from 'react';
2import styled, { keyframes, css, ThemeProvider, createGlobalStyle } from 'styled-components';
3
4// ===== THEMES =====
5const darkTheme = {
6 bg: '#0a0a1a',
7 surface: '#12122a',
8 surfaceHover: '#1a1a3a',
9 accent: '#00ff88',
10 accentDim: 'rgba(0,255,136,0.15)',
11 text: '#e0e0e0',
12 textSecondary: '#8888aa',
13 danger: '#ff4444',
14 warning: '#ffaa00',
15 border: 'rgba(0,255,136,0.2)',
16};
17
18const lightTheme = {
19 bg: '#f0f4f8',
20 surface: '#ffffff',
21 surfaceHover: '#e8ecf0',
22 accent: '#0066ff',
23 accentDim: 'rgba(0,102,255,0.1)',
24 text: '#1a1a2e',
25 textSecondary: '#666688',
26 danger: '#cc0000',
27 warning: '#cc8800',
28 border: 'rgba(0,102,255,0.2)',
29};
30
31// ===== GLOBAL STYLES =====
32const GlobalStyle = createGlobalStyle`
33 * { margin: 0; padding: 0; box-sizing: border-box; }
34 body {
35 font-family: 'Segoe UI', Tahoma, sans-serif;
36 background: ${props => props.theme.bg};
37 color: ${props => props.theme.text};
38 transition: background 0.3s ease, color 0.3s ease;
39 }
40`;
41
42// ===== ANIMATIONS =====
43const pulse = keyframes`
44 0%, 100% { opacity: 1; }
45 50% { opacity: 0.5; }
46`;
47
48const slideIn = keyframes`
49 from { opacity: 0; transform: translateY(20px); }
50 to { opacity: 1; transform: translateY(0); }
51`;
52
53const rotate = keyframes`
54 from { transform: rotate(0deg); }
55 to { transform: rotate(360deg); }
56`;
57
58const glow = keyframes`
59 0%, 100% { box-shadow: 0 0 5px ${() => 'rgba(0,255,136,0.3)'}; }
60 50% { box-shadow: 0 0 20px ${() => 'rgba(0,255,136,0.6)'}; }
61`;
62
63// ===== LAYOUT COMPONENTS =====
64const Header = styled.header`
65 display: flex;
66 justify-content: space-between;
67 align-items: center;
68 padding: 16px 24px;
69 background: ${p => p.theme.surface};
70 border-bottom: 1px solid ${p => p.theme.border};
71`;
72
73const Title = styled.h1`
74 font-size: 20px;
75 color: ${p => p.theme.accent};
76 letter-spacing: 2px;
77 text-transform: uppercase;
78`;
79
80const ThemeToggle = styled.button`
81 background: ${p => p.theme.accentDim};
82 border: 1px solid ${p => p.theme.accent};
83 color: ${p => p.theme.accent};
84 padding: 8px 16px;
85 border-radius: 8px;
86 cursor: pointer;
87 font-size: 14px;
88 transition: all 0.3s ease;
89 &:hover {
90 background: ${p => p.theme.accent};
91 color: ${p => p.theme.bg};
92 }
93`;
94
95const DashboardGrid = styled.div`
96 display: grid;
97 grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
98 gap: 20px;
99 padding: 24px;
100`;
101
102const Card = styled.div`
103 background: ${p => p.theme.surface};
104 border: 1px solid ${p => p.theme.border};
105 border-radius: 12px;
106 padding: 20px;
107 animation: ${slideIn} 0.5s ease ${p => p.delay || '0s'} both;
108 transition: transform 0.3s ease, box-shadow 0.3s ease;
109 &:hover {
110 transform: translateY(-4px);
111 box-shadow: 0 8px 25px ${p => p.theme.accentDim};
112 }
113`;
114
115const CardTitle = styled.h3`
116 font-size: 14px;
117 text-transform: uppercase;
118 letter-spacing: 1px;
119 color: ${p => p.theme.accent};
120 margin-bottom: 16px;
121 display: flex;
122 align-items: center;
123 gap: 8px;
124`;
125
126// ===== STATUS COMPONENTS =====
127const StatusBadge = styled.span`
128 display: inline-block;
129 padding: 3px 10px;
130 border-radius: 12px;
131 font-size: 11px;
132 font-weight: bold;
133 background: ${p => p.active ? 'rgba(0,255,136,0.15)' : 'rgba(255,68,68,0.15)'};
134 color: ${p => p.active ? '#00ff88' : '#ff4444'};
135 animation: ${pulse} 2s infinite;
136`;
137
138const SystemRow = styled.div`
139 display: flex;
140 justify-content: space-between;
141 align-items: center;
142 padding: 8px 0;
143 border-bottom: 1px solid ${p => p.theme.border};
144 &:last-child { border-bottom: none; }
145`;
146
147// ===== RADAR COMPONENTS =====
148const RadarWrapper = styled.div`
149 width: 120px;
150 height: 120px;
151 border-radius: 50%;
152 border: 2px solid ${p => p.theme.accent};
153 position: relative;
154 margin: 0 auto 16px;
155 overflow: hidden;
156 background: radial-gradient(circle, ${p => p.theme.accentDim} 0%, transparent 70%);
157`;
158
159const RadarSweep = styled.div`
160 position: absolute;
161 top: 0; left: 50%;
162 width: 2px; height: 50%;
163 background: ${p => p.theme.accent};
164 transform-origin: bottom center;
165 animation: ${rotate} 3s linear infinite;
166`;
167
168const RadarDot = styled.div`
169 position: absolute;
170 width: 6px; height: 6px;
171 background: ${p => p.theme.accent};
172 border-radius: 50%;
173 top: ${p => p.y}%;
174 left: ${p => p.x}%;
175 animation: ${pulse} 1.5s infinite;
176`;
177
178// ===== PROGRESS BAR =====
179const ProgressOuter = styled.div`
180 width: 100%;
181 height: 6px;
182 background: ${p => p.theme.border};
183 border-radius: 3px;
184 margin-top: 6px;
185`;
186
187const ProgressInner = styled.div`
188 height: 100%;
189 width: ${p => p.value || 0}%;
190 background: ${p =>
191 p.value > 75 ? '#00ff88' :
192 p.value > 40 ? '#ffaa00' : '#ff4444'};
193 border-radius: 3px;
194 transition: width 0.5s ease;
195`;
196
197// ===== CREW AVATAR =====
198const AvatarCircle = styled.div`
199 width: 36px;
200 height: 36px;
201 border-radius: 50%;
202 background: ${p => p.color || p.theme.accent};
203 display: flex;
204 align-items: center;
205 justify-content: center;
206 font-size: 14px;
207 font-weight: bold;
208 color: #fff;
209`;
210
211const CrewRow = styled.div`
212 display: flex;
213 align-items: center;
214 gap: 12px;
215 padding: 8px 0;
216`;
217
218const CrewInfo = styled.div`
219 flex: 1;
220 & span { display: block; font-size: 12px; color: ${p => p.theme.textSecondary}; }
221`;
222
223// ===== DATA =====
224const systems = [
225 { name: 'Warp engines', active: true },
226 { name: 'Energy shields', active: true },
227 { name: 'Navigation systems', active: true },
228 { name: 'Long-range communications', active: false },
229 { name: 'Life support', active: true },
230];
231
232const missions = [
233 { name: 'X-7 Nebula Exploration', progress: 87 },
234 { name: 'Supply Transport', progress: 45 },
235 { name: 'Asteroid Mapping', progress: 62 },
236 { name: 'Relay Station Repair', progress: 15 },
237];
238
239const crew = [
240 { name: 'Cpt. Nova', role: 'Commander', color: '#e74c3c', initial: 'N' },
241 { name: 'Lt. Orion', role: 'Navigator', color: '#3498db', initial: 'O' },
242 { name: 'Dr. Stella', role: 'Medic', color: '#9b59b6', initial: 'S' },
243 { name: 'Ing. Vega', role: 'Engineer', color: '#2ecc71', initial: 'V' },
244];
245
246// ===== APP =====
247function App() {
248 const [isDark, setIsDark] = useState(true);
249 const theme = isDark ? darkTheme : lightTheme;
250
251 return (
252 <ThemeProvider theme={theme}>
253 <GlobalStyle />
254 <Header>
255 <Title>Mission Control</Title>
256 <ThemeToggle onClick={() => setIsDark(!isDark)}>
257 {isDark ? 'Light Mode' : 'Dark Mode'}
258 </ThemeToggle>
259 </Header>
260
261 <DashboardGrid>
262 {/* Systems Status */}
263 <Card delay="0s">
264 <CardTitle>Ship systems</CardTitle>
265 {systems.map((s, i) => (
266 <SystemRow key={i}>
267 <span>{s.name}</span>
268 <StatusBadge active={s.active}>
269 {s.active ? 'ONLINE' : 'OFFLINE'}
270 </StatusBadge>
271 </SystemRow>
272 ))}
273 </Card>
274
275 {/* Radar */}
276 <Card delay="0.1s">
277 <CardTitle>Radar</CardTitle>
278 <RadarWrapper>
279 <RadarSweep />
280 <RadarDot x={30} y={25} />
281 <RadarDot x={65} y={40} />
282 <RadarDot x={45} y={70} />
283 </RadarWrapper>
284 <p style={{ textAlign: 'center', fontSize: 13, opacity: 0.7 }}>
285 3 objects within scanner range
286 </p>
287 </Card>
288
289 {/* Missions Progress */}
290 <Card delay="0.2s">
291 <CardTitle>Missions</CardTitle>
292 {missions.map((m, i) => (
293 <div key={i} style={{ marginBottom: 12 }}>
294 <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 13 }}>
295 <span>{m.name}</span>
296 <span style={{ fontWeight: 'bold' }}>{m.progress}%</span>
297 </div>
298 <ProgressOuter>
299 <ProgressInner value={m.progress} />
300 </ProgressOuter>
301 </div>
302 ))}
303 </Card>
304
305 {/* Crew */}
306 <Card delay="0.3s">
307 <CardTitle>Crew</CardTitle>
308 {crew.map((c, i) => (
309 <CrewRow key={i}>
310 <AvatarCircle color={c.color}>{c.initial}</AvatarCircle>
311 <CrewInfo>
312 <strong>{c.name}</strong>
313 <span>{c.role}</span>
314 </CrewInfo>
315 <StatusBadge active={true}>On duty</StatusBadge>
316 </CrewRow>
317 ))}
318 </Card>
319 </DashboardGrid>
320 </ThemeProvider>
321 );
322}
323
324export default App;Spotted a mistake in this lesson?
Hands-on tasks in the game
- Code editor
Build a comprehensive Design System including: styled-components for component-level styling, CSS-in-JS patterns, theme provider with dark/light modes, responsive design with breakpoints, CSS Grid and Flexbox layouts, animations with @keyframes, typography system, color tokens, spacing system, and accessibility features (focus states, ARIA). Create a design system worthy of the most advanced Deep Space Nine interfaces!