Skip to content

Commit 03a1569

Browse files
ChengaDevclaude
andcommitted
Remove Bootstrap, rewrite Navigation with styled-components
- Replace react-bootstrap Nav/Navbar with pure styled-components - Add hamburger menu with slide-down animation on mobile - Add minimal Bootstrap-compatible CSS reset to GlobalStyle - Fix clock digit vertical centering with flexbox - Fix flag images by removing height: auto from img reset Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 8a6a905 commit 03a1569

4 files changed

Lines changed: 212 additions & 125 deletions

File tree

src/App.tsx

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,50 @@ const GlobalStyle = createGlobalStyle`
180180
background: ${props => props.theme.mainBackgroundColor};
181181
}
182182
183-
* {
183+
*, *::before, *::after {
184184
box-sizing: border-box;
185185
}
186+
187+
/* Bootstrap base reset */
188+
p {
189+
margin-top: 0;
190+
margin-bottom: 1rem;
191+
}
192+
193+
h1, h2, h3, h4, h5, h6 {
194+
margin-top: 0;
195+
margin-bottom: 0.5rem;
196+
font-weight: 500;
197+
line-height: 1.2;
198+
}
199+
200+
ul, ol {
201+
margin-top: 0;
202+
margin-bottom: 1rem;
203+
padding-left: 2rem;
204+
}
205+
206+
a {
207+
text-decoration: none;
208+
}
209+
210+
img {
211+
max-width: 100%;
212+
vertical-align: middle;
213+
border-style: none;
214+
}
215+
216+
button, input, select, textarea {
217+
font-family: inherit;
218+
font-size: inherit;
219+
line-height: inherit;
220+
}
221+
222+
hr {
223+
margin: 1rem 0;
224+
border: 0;
225+
border-top: 1px solid rgba(0, 0, 0, 0.1);
226+
}
186227
`
187228

188229
const AppContainer = styled.div`

src/components/Navigation.tsx

Lines changed: 167 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { Nav, Navbar } from 'react-bootstrap'
2-
import styled from 'styled-components'
1+
import styled, { keyframes } from 'styled-components'
32
import Themes from '../constants/Themes'
43
import AppRoutes from '../AppRoutes'
54
import { Link, useLocation } from 'react-router-dom'
@@ -36,132 +35,118 @@ const Navigation = ({ currentTheme, setTheme }: NavigationProps) => {
3635
}
3736

3837
return (
39-
<Container ref={navRef}>
40-
<Navbar
41-
variant={currentTheme === Themes.Light ? 'light' : 'dark'}
42-
expand="lg"
43-
fixed="top"
44-
expanded={expanded}
45-
onToggle={setExpanded}
38+
<NavBar ref={navRef}>
39+
<Brand to={routes.Home}>
40+
<BrandText>ShotClock Pro</BrandText>
41+
</Brand>
42+
<Hamburger
43+
onClick={() => setExpanded(!expanded)}
44+
aria-label="Toggle navigation"
45+
aria-expanded={expanded}
46+
aria-controls="nav-menu"
4647
>
47-
<Navbar.Brand as={Link} to={routes.Home}>
48-
<BrandText>ShotClock Pro</BrandText>
49-
</Navbar.Brand>
50-
<Navbar.Toggle aria-controls="basic-navbar-nav" />
51-
<Navbar.Collapse id="basic-navbar-nav">
52-
<Nav className="ml-auto">
53-
<NavLink
54-
to={routes.Home}
55-
$active={location.pathname === routes.Home}
56-
onClick={handleNavClick}
48+
<HamburgerLine />
49+
<HamburgerLine />
50+
<HamburgerLine />
51+
</Hamburger>
52+
<NavCollapse id="nav-menu" $expanded={expanded}>
53+
<NavItems>
54+
<NavLink
55+
to={routes.Home}
56+
$active={location.pathname === routes.Home}
57+
onClick={handleNavClick}
58+
>
59+
{locals.home}
60+
</NavLink>
61+
<NavLink
62+
to={routes.Instructions}
63+
$active={location.pathname === routes.Instructions}
64+
onClick={handleNavClick}
65+
>
66+
{locals.instructions}
67+
</NavLink>
68+
<NavLink
69+
to={routes.FIBAResources}
70+
$active={location.pathname === routes.FIBAResources}
71+
onClick={handleNavClick}
72+
>
73+
{locals.fibaResources}
74+
</NavLink>
75+
<NavLink
76+
to={routes.About}
77+
$active={location.pathname === routes.About}
78+
onClick={handleNavClick}
79+
>
80+
{locals.about}
81+
</NavLink>
82+
<NavLink
83+
to={routes.FAQ}
84+
$active={location.pathname === routes.FAQ}
85+
onClick={handleNavClick}
86+
>
87+
{locals.faq}
88+
</NavLink>
89+
<ThemeToggle>
90+
<ThemeButton
91+
$active={currentTheme === Themes.Light}
92+
onClick={() => setTheme(Themes.Light)}
5793
>
58-
{locals.home}
59-
</NavLink>
60-
<NavLink
61-
to={routes.Instructions}
62-
$active={location.pathname === routes.Instructions}
63-
onClick={handleNavClick}
94+
light
95+
</ThemeButton>
96+
<ThemeDivider>|</ThemeDivider>
97+
<ThemeButton
98+
$active={currentTheme === Themes.Dark}
99+
onClick={() => setTheme(Themes.Dark)}
64100
>
65-
{locals.instructions}
66-
</NavLink>
67-
<NavLink
68-
to={routes.FIBAResources}
69-
$active={location.pathname === routes.FIBAResources}
70-
onClick={handleNavClick}
71-
>
72-
{locals.fibaResources}
73-
</NavLink>
74-
<NavLink
75-
to={routes.About}
76-
$active={location.pathname === routes.About}
77-
onClick={handleNavClick}
78-
>
79-
{locals.about}
80-
</NavLink>
81-
<NavLink
82-
to={routes.FAQ}
83-
$active={location.pathname === routes.FAQ}
84-
onClick={handleNavClick}
85-
>
86-
{locals.faq}
87-
</NavLink>
88-
<ThemeToggle>
89-
<ThemeButton
90-
$active={currentTheme === Themes.Light}
91-
onClick={() => setTheme(Themes.Light)}
92-
>
93-
light
94-
</ThemeButton>
95-
<ThemeDivider>|</ThemeDivider>
96-
<ThemeButton
97-
$active={currentTheme === Themes.Dark}
98-
onClick={() => setTheme(Themes.Dark)}
99-
>
100-
dark
101-
</ThemeButton>
102-
</ThemeToggle>
103-
</Nav>
104-
</Navbar.Collapse>
105-
</Navbar>
106-
</Container>
101+
dark
102+
</ThemeButton>
103+
</ThemeToggle>
104+
</NavItems>
105+
</NavCollapse>
106+
</NavBar>
107107
)
108108
}
109109

110-
const Container = styled.div`
111-
nav {
112-
background: rgba(0, 0, 0, 0.8) !important;
113-
backdrop-filter: blur(10px);
114-
-webkit-backdrop-filter: blur(10px);
115-
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
116-
padding: 0.5rem 1rem;
117-
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.3);
118-
font-family: 'Poppins', sans-serif;
119-
z-index: 1000;
120-
}
121-
122-
.navbar-brand {
123-
font-size: 1.8rem;
124-
line-height: 1.5;
125-
color: ${(props) => props.theme.primary} !important;
126-
transition: transform 0.2s ease-in-out;
127-
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
128-
129-
&:hover {
130-
transform: scale(1.05);
131-
}
110+
const slideDown = keyframes`
111+
from {
112+
opacity: 0;
113+
transform: translateY(-8px);
132114
}
133-
134-
.navbar-toggler {
135-
border: none;
136-
padding: 0.5rem;
137-
&:focus {
138-
outline: none;
139-
box-shadow: none;
140-
}
115+
to {
116+
opacity: 1;
117+
transform: translateY(0);
141118
}
119+
`
142120

143-
.navbar-toggler-icon {
144-
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(255, 255, 255, 0.9)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
145-
}
121+
const NavBar = styled.nav`
122+
position: fixed;
123+
top: 0;
124+
left: 0;
125+
right: 0;
126+
z-index: 1000;
127+
display: flex;
128+
align-items: center;
129+
flex-wrap: wrap;
130+
gap: 1rem;
131+
min-height: 70px;
132+
background: rgba(0, 0, 0, 0.8);
133+
backdrop-filter: blur(10px);
134+
-webkit-backdrop-filter: blur(10px);
135+
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
136+
padding: 0.5rem 1rem;
137+
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.3);
138+
font-family: 'Poppins', sans-serif;
139+
`
146140

147-
@media (max-width: 768px) {
148-
nav {
149-
margin: 0;
150-
border-radius: 0;
151-
}
141+
const Brand = styled(Link)`
142+
font-size: 1.8rem;
143+
line-height: 1.5;
144+
text-decoration: none;
145+
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
146+
transition: transform 0.2s ease-in-out;
152147
153-
.navbar-collapse {
154-
background: rgba(0, 0, 0, 0.9);
155-
backdrop-filter: blur(10px);
156-
-webkit-backdrop-filter: blur(10px);
157-
border-radius: 0 0 1rem 1rem;
158-
padding: 1rem;
159-
position: absolute;
160-
top: 100%;
161-
left: 0;
162-
right: 0;
163-
z-index: 1000;
164-
}
148+
&:hover {
149+
transform: scale(1.05);
165150
}
166151
`
167152

@@ -171,11 +156,70 @@ const BrandText = styled.span`
171156
background: linear-gradient(45deg, #ffd700, #ff6b6b);
172157
-webkit-background-clip: text;
173158
-webkit-text-fill-color: transparent;
174-
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
159+
`
160+
161+
const Hamburger = styled.button`
162+
display: none;
163+
flex-direction: column;
164+
gap: 5px;
165+
background: none;
166+
border: none;
167+
padding: 0.5rem;
168+
cursor: pointer;
169+
margin-left: auto;
170+
171+
&:focus {
172+
outline: none;
173+
box-shadow: none;
174+
}
175+
176+
@media (max-width: 768px) {
177+
display: flex;
178+
}
179+
`
180+
181+
const HamburgerLine = styled.span`
182+
display: block;
183+
width: 22px;
184+
height: 2px;
185+
background: rgba(255, 255, 255, 0.9);
186+
border-radius: 2px;
187+
`
188+
189+
const NavCollapse = styled.div<{ $expanded: boolean }>`
190+
display: flex;
191+
align-items: center;
192+
193+
@media (max-width: 768px) {
194+
display: ${(props) => (props.$expanded ? 'flex' : 'none')};
195+
flex-direction: column;
196+
width: 100%;
197+
margin-left: 0;
198+
background: rgba(0, 0, 0, 0.9);
199+
backdrop-filter: blur(10px);
200+
-webkit-backdrop-filter: blur(10px);
201+
border-radius: 0 0 1rem 1rem;
202+
padding: 1rem;
203+
position: absolute;
204+
top: 100%;
205+
left: 0;
206+
right: 0;
207+
animation: ${slideDown} 0.35s ease-out;
208+
}
209+
`
210+
211+
const NavItems = styled.div`
212+
display: flex;
213+
align-items: center;
214+
215+
@media (max-width: 768px) {
216+
flex-direction: column;
217+
width: 100%;
218+
}
175219
`
176220

177221
const NavLink = styled(Link)<{ $active: boolean }>`
178-
color: #ffffff !important;
222+
color: #ffffff;
179223
margin: 0 1rem;
180224
text-decoration: none;
181225
font-weight: 500;
@@ -197,7 +241,7 @@ const NavLink = styled(Link)<{ $active: boolean }>`
197241
}
198242
199243
&:hover {
200-
color: #ffd700 !important;
244+
color: #ffd700;
201245
&:after {
202246
width: 100%;
203247
}
@@ -207,8 +251,8 @@ const NavLink = styled(Link)<{ $active: boolean }>`
207251
margin: 0.5rem 0;
208252
padding: 0.5rem 1rem;
209253
border-radius: 8px;
210-
background: ${(props) =>
211-
props.$active ? 'rgba(255, 255, 255, 0.1)' : 'transparent'};
254+
width: 100%;
255+
background: ${(props) => (props.$active ? 'rgba(255, 255, 255, 0.1)' : 'transparent')};
212256
213257
&:after {
214258
display: none;
@@ -231,6 +275,7 @@ const ThemeToggle = styled.div`
231275
@media (max-width: 768px) {
232276
margin: 0.5rem 0;
233277
justify-content: center;
278+
width: 100%;
234279
}
235280
`
236281

0 commit comments

Comments
 (0)