Skip to content

Commit fb356cb

Browse files
ChengaDevclaude
andcommitted
Fix light mode: theme tokens, footer nav links, persist theme
- Add accent, cardBorder, cardText, subtleText theme tokens - Replace all hardcoded dark card colors across FAQ, Instructions, FIBAResources, AboutUs, SeeAlso, PrivacyPolicy with theme tokens - ContentCard now uses cardBackground (theme-aware) - PageBlurb and SeeAlso use subtleText token - Footer internal links use React Router Link (no full-page reload) - Theme choice persisted to localStorage so it survives navigation - FAQ faqSchema moved to SEO schema prop (removes duplicate Helmet) - Add Instruction.md to .gitignore Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent c0c7258 commit fb356cb

13 files changed

Lines changed: 123 additions & 109 deletions

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,6 @@
2121
npm-debug.log*
2222
yarn-debug.log*
2323
yarn-error.log*
24+
25+
# Local notes
26+
Instruction.md

Instruction.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
In this repo there is a simple application for practicing in opretaing a basketball shotclock.
2+
3+
Please take this application one step forward and wrap it so it will be looking like a modern web application that have more content such as:
4+
FIBA resources links
5+
About Us
6+
Instructions for operating the shot clock
7+
8+
Please make a menu and pages that can fit PC and mobile.
9+
10+
11+
Please take into consideration that in the future the app is planned to have 2 modes:
12+
1. Simple and free more as it is today
13+
2. Advance mode, which would enable to create scenarios based predefined series of actions of a basketball game and measture the response time of the user. It should not be implemented now but please take into considration in your design.
14+
15+
The design should fit the current one, if you can improve it - go for it.

src/App.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,17 +120,26 @@ const pageRoutes = (
120120
</>
121121
)
122122

123+
const THEME_KEY = 'sc_theme'
124+
123125
const App = () => {
124-
const [selectedTheme, setSelectedTheme] = useState(Themes.Dark)
126+
const [selectedTheme, setSelectedTheme] = useState<string>(
127+
() => localStorage.getItem(THEME_KEY) ?? Themes.Dark
128+
)
125129
const theme = selectedTheme === Themes.Light ? lightTheme : darkTheme
126130

131+
const handleSetTheme = (t: string) => {
132+
localStorage.setItem(THEME_KEY, t)
133+
setSelectedTheme(t)
134+
}
135+
127136
return (
128137
<ThemeProvider theme={theme}>
129138
<Router>
130139
<LanguageProvider>
131140
<GlobalStyle />
132141
<AppContainer>
133-
<Navigation currentTheme={selectedTheme} setTheme={setSelectedTheme} />
142+
<Navigation currentTheme={selectedTheme} setTheme={handleSetTheme} />
134143
<PageWrapper>
135144
<MainContent>
136145
<Suspense fallback={null}>
@@ -209,16 +218,15 @@ const MainContent = styled.main`
209218
`
210219

211220
const ContentCard = styled.div`
212-
background: ${props => props.theme.mainBackgroundColor};
221+
background: ${props => props.theme.cardBackground};
213222
border-radius: 12px;
214223
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
215224
padding: 2rem;
216225
margin: 2rem auto;
217226
max-width: 1200px;
218227
width: 90%;
219228
backdrop-filter: blur(10px);
220-
background-color: rgba(255, 255, 255, 0.1);
221-
border: 1px solid rgba(255, 255, 255, 0.2);
229+
border: 1px solid ${props => props.theme.cardBorder};
222230
transform: translateY(0);
223231
transition: transform 0.3s ease-in-out, box-shadow 0.3s ease-in-out;
224232

src/components/AboutUs.tsx

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -135,22 +135,21 @@ const Features = styled.div`
135135
`
136136

137137
const FeatureCard = styled.div`
138-
background: rgba(0, 0, 0, 0.6);
138+
background: ${props => props.theme.cardBackground};
139139
border-radius: 16px;
140140
padding: 2rem;
141141
text-align: center;
142142
transition: all 0.3s ease;
143-
border: 1px solid rgba(255, 255, 255, 0.1);
143+
border: 1px solid ${props => props.theme.cardBorder};
144144
backdrop-filter: blur(10px);
145145
animation: ${fadeInUp} 0.6s ease-out forwards;
146146
opacity: 0;
147147
animation-delay: 0.6s;
148148
149149
&:hover {
150150
transform: translateY(-5px);
151-
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.3);
152-
border-color: rgba(255, 215, 0, 0.3);
153-
background: rgba(0, 0, 0, 0.7);
151+
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.15);
152+
border-color: ${props => props.theme.accent};
154153
}
155154
`
156155

@@ -161,19 +160,16 @@ const FeatureIcon = styled.div`
161160

162161
const FeatureTitle = styled.h3`
163162
font-size: 1.3rem;
164-
color: #ffd700;
163+
color: ${props => props.theme.titleColor};
165164
margin-bottom: 1rem;
166165
font-weight: 600;
167-
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
168-
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));
169166
`
170167

171168
const FeatureText = styled.p`
172-
color: rgba(255, 255, 255, 0.95);
169+
color: ${props => props.theme.cardText};
173170
margin: 0;
174171
line-height: 1.6;
175172
font-size: 1rem;
176-
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
177173
`
178174

179175
const ContactSection = styled.div`
@@ -183,13 +179,13 @@ const ContactSection = styled.div`
183179

184180
const ContactTitle = styled.h2`
185181
font-size: 1.8rem;
186-
color: #ffd700;
182+
color: ${props => props.theme.titleColor};
187183
margin-bottom: 1rem;
188184
font-weight: 700;
189185
`
190186

191187
const ContactText = styled.p`
192-
color: rgba(255, 255, 255, 0.75);
188+
color: ${props => props.theme.cardText};
193189
font-size: 1rem;
194190
margin-bottom: 2rem;
195191
`
@@ -207,16 +203,16 @@ const ContactLink = styled.a`
207203
gap: 0.5rem;
208204
padding: 0.65rem 1.5rem;
209205
border-radius: 8px;
210-
border: 1px solid rgba(255, 215, 0, 0.35);
211-
color: #ffd700;
206+
border: 1px solid ${props => props.theme.accent}88;
207+
color: ${props => props.theme.accent};
212208
font-size: 0.95rem;
213209
font-weight: 600;
214210
text-decoration: none;
215211
transition: all 0.2s ease;
216212
217213
&:hover {
218-
background: rgba(255, 215, 0, 0.1);
219-
border-color: #ffd700;
214+
background: ${props => props.theme.accent}18;
215+
border-color: ${props => props.theme.accent};
220216
}
221217
`
222218

src/components/FAQ.tsx

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import React, { useState } from 'react'
22
import styled, { keyframes } from 'styled-components'
3-
import { Helmet } from 'react-helmet'
43
import { useLocalization } from '../contexts/Language/LanguageProvider'
54
import SEO from './SEO'
65
import SeeAlso from './SeeAlso'
@@ -36,10 +35,8 @@ const FAQ = () => {
3635
<SEO
3736
title="FAQ | ShotClock Pro"
3837
description="Frequently asked questions about basketball shot clock rules, the 14-second reset, and how to use the ShotClock Pro training simulator."
38+
schema={faqSchema}
3939
/>
40-
<Helmet>
41-
<script type="application/ld+json">{JSON.stringify(faqSchema)}</script>
42-
</Helmet>
4340

4441
<AnimatedTitle>{locals.faqTitle}</AnimatedTitle>
4542
<Description>{locals.faqDescription}</Description>
@@ -103,16 +100,16 @@ const FaqList = styled.div`
103100
`
104101

105102
const FaqItem = styled.div`
106-
background: rgba(0, 0, 0, 0.5);
103+
background: ${props => props.theme.cardBackground};
107104
border-radius: 12px;
108-
border: 1px solid rgba(255, 255, 255, 0.1);
105+
border: 1px solid ${props => props.theme.cardBorder};
109106
overflow: hidden;
110107
animation: ${fadeInUp} 0.5s ease-out forwards;
111108
opacity: 0;
112109
transition: border-color 0.2s ease;
113110
114111
&:hover {
115-
border-color: rgba(255, 215, 0, 0.3);
112+
border-color: ${props => props.theme.accent};
116113
}
117114
`
118115

@@ -143,7 +140,7 @@ const QuestionText = styled.span`
143140
`
144141

145142
const Chevron = styled.span<{ isOpen: boolean }>`
146-
color: #ffd700;
143+
color: ${props => props.theme.accent};
147144
font-size: 1.3rem;
148145
flex-shrink: 0;
149146
transform: ${props => props.isOpen ? 'rotate(180deg)' : 'rotate(0deg)'};
@@ -154,10 +151,10 @@ const Chevron = styled.span<{ isOpen: boolean }>`
154151
const Answer = styled.p`
155152
padding: 0 1.5rem 1.25rem;
156153
margin: 0;
157-
color: rgba(255, 255, 255, 0.85);
154+
color: ${props => props.theme.cardText};
158155
font-size: 1rem;
159156
line-height: 1.7;
160-
border-top: 1px solid rgba(255, 255, 255, 0.07);
157+
border-top: 1px solid ${props => props.theme.cardBorder};
161158
padding-top: 1rem;
162159
`
163160

src/components/FIBAResources.tsx

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -151,20 +151,20 @@ const ResourcesGrid = styled.div`
151151
`
152152

153153
const ResourceArrow = styled.span`
154-
color: #ffd700;
154+
color: ${props => props.theme.accent};
155155
font-size: 1.5rem;
156156
transition: transform 0.3s ease;
157157
`
158158

159159
const ResourceCard = styled.a`
160-
background: rgba(0, 0, 0, 0.6);
160+
background: ${props => props.theme.cardBackground};
161161
border-radius: 16px;
162162
padding: 2rem;
163163
display: flex;
164164
align-items: center;
165165
gap: 1.5rem;
166166
text-decoration: none;
167-
border: 1px solid rgba(255, 255, 255, 0.1);
167+
border: 1px solid ${props => props.theme.cardBorder};
168168
transition: all 0.3s ease;
169169
position: relative;
170170
overflow: hidden;
@@ -173,30 +173,13 @@ const ResourceCard = styled.a`
173173
174174
&:hover {
175175
transform: translateY(-5px);
176-
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.3);
177-
border-color: rgba(255, 215, 0, 0.3);
178-
background: rgba(0, 0, 0, 0.7);
176+
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.15);
177+
border-color: ${props => props.theme.accent};
179178
180179
${ResourceArrow} {
181180
transform: translateX(5px);
182181
}
183182
}
184-
185-
&::before {
186-
content: '';
187-
position: absolute;
188-
top: 0;
189-
left: 0;
190-
width: 100%;
191-
height: 100%;
192-
background: linear-gradient(45deg, rgba(255, 215, 0, 0.1), rgba(255, 107, 107, 0.1));
193-
opacity: 0;
194-
transition: opacity 0.3s ease;
195-
}
196-
197-
&:hover::before {
198-
opacity: 1;
199-
}
200183
`
201184

202185
const ResourceIcon = styled.div`
@@ -209,20 +192,17 @@ const ResourceContent = styled.div`
209192
`
210193

211194
const ResourceTitle = styled.h3`
212-
color: #ffd700;
195+
color: ${props => props.theme.titleColor};
213196
font-size: 1.3rem;
214197
margin: 0 0 0.5rem;
215198
font-weight: 600;
216-
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.8);
217-
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.8));
218199
`
219200

220201
const ResourceDescription = styled.p`
221-
color: rgba(255, 255, 255, 0.95);
202+
color: ${props => props.theme.cardText};
222203
margin: 0;
223204
font-size: 0.95rem;
224205
line-height: 1.5;
225-
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.8);
226206
`
227207

228208
const AdditionalInfo = styled.div`
@@ -250,20 +230,19 @@ const InfoGrid = styled.div`
250230
`
251231

252232
const InfoCard = styled.div`
253-
background: rgba(0, 0, 0, 0.6);
233+
background: ${props => props.theme.cardBackground};
254234
border-radius: 16px;
255235
padding: 2rem;
256236
text-align: center;
257-
border: 1px solid rgba(255, 255, 255, 0.1);
237+
border: 1px solid ${props => props.theme.cardBorder};
258238
transition: all 0.3s ease;
259239
animation: ${fadeInUp} 0.6s ease-out forwards;
260240
opacity: 0;
261241
262242
&:hover {
263243
transform: translateY(-5px);
264-
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.3);
265-
border-color: rgba(255, 215, 0, 0.3);
266-
background: rgba(0, 0, 0, 0.7);
244+
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.15);
245+
border-color: ${props => props.theme.accent};
267246
}
268247
`
269248

@@ -273,11 +252,10 @@ const InfoIcon = styled.div`
273252
`
274253

275254
const InfoText = styled.p`
276-
color: rgba(255, 255, 255, 0.95);
255+
color: ${props => props.theme.cardText};
277256
margin: 0;
278257
line-height: 1.6;
279258
font-size: 1rem;
280-
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
281259
`
282260

283261
export default FIBAResources

src/components/Footer.tsx

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import styled from 'styled-components'
2+
import { Link } from 'react-router-dom'
23
import DonateButton from './DonateButton'
34
import LanguageSelector from './LanguageSelector'
45
import { useLocalization } from '../contexts/Language/LanguageProvider'
6+
import AppRoutes from '../AppRoutes'
57

68
const Footer = () => {
7-
const { locals } = useLocalization()
9+
const { locals, languageCode } = useLocalization()
10+
const routes = AppRoutes(languageCode)
811

912
return (
1013
<Container>
@@ -15,15 +18,15 @@ const Footer = () => {
1518
{locals.aboutContent[0]}
1619
</FooterText>
1720
</FooterSection>
18-
21+
1922
<FooterSection>
2023
<FooterTitle>Quick Links</FooterTitle>
2124
<FooterLinks>
22-
<FooterLink href="/instructions">{locals.instructions}</FooterLink>
23-
<FooterLink href="/fiba-resources">{locals.fibaResources}</FooterLink>
24-
<FooterLink href="/faq">{locals.faq}</FooterLink>
25-
<FooterLink href="/about">{locals.about}</FooterLink>
26-
<FooterLink href="/privacy-policy">Privacy Policy</FooterLink>
25+
<FooterNavLink to={routes.Instructions}>{locals.instructions}</FooterNavLink>
26+
<FooterNavLink to={routes.FIBAResources}>{locals.fibaResources}</FooterNavLink>
27+
<FooterNavLink to={routes.FAQ}>{locals.faq}</FooterNavLink>
28+
<FooterNavLink to={routes.About}>{locals.about}</FooterNavLink>
29+
<FooterNavLink to="/privacy-policy">Privacy Policy</FooterNavLink>
2730
</FooterLinks>
2831
</FooterSection>
2932

@@ -97,7 +100,7 @@ const FooterLinks = styled.div`
97100
gap: 0.5rem;
98101
`
99102

100-
const FooterLink = styled.a`
103+
const footerLinkStyles = `
101104
color: rgba(255, 255, 255, 0.8);
102105
text-decoration: none;
103106
font-size: 0.9rem;
@@ -124,6 +127,14 @@ const FooterLink = styled.a`
124127
}
125128
`
126129

130+
const FooterNavLink = styled(Link)`
131+
${footerLinkStyles}
132+
`
133+
134+
const FooterLink = styled.a`
135+
${footerLinkStyles}
136+
`
137+
127138
const FooterBottom = styled.div`
128139
max-width: 1200px;
129140
margin: 0 auto;

0 commit comments

Comments
 (0)