Skip to content

Commit 020fc5a

Browse files
committed
feat: introduce responsive UI with a new dark theme, improved forms, and polished layout for weather and report pages
1 parent 3191a90 commit 020fc5a

File tree

2 files changed

+282
-142
lines changed

2 files changed

+282
-142
lines changed

src/main/resources/templates/index.html

Lines changed: 152 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -6,123 +6,194 @@
66
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
77
<title>Weather App (Spring Boot + Thymeleaf + LangChain4j)</title>
88
<style>
9+
:root {
10+
color-scheme: light dark;
11+
--bg: #0b1020;
12+
--panel: #141a2f;
13+
--text: #e6e9f0;
14+
--muted: #9aa3b2;
15+
--primary: #4f8cff;
16+
--primary-contrast: #ffffff;
17+
--border: #23304d;
18+
--focus: #a3c5ff;
19+
}
20+
21+
@media (prefers-color-scheme: light) {
22+
:root {
23+
--bg: #f7f9fc;
24+
--panel: #ffffff;
25+
--text: #172033;
26+
--muted: #51607a;
27+
--primary: #0d6efd;
28+
--primary-contrast: #ffffff;
29+
--border: #e5eaf2;
30+
--focus: #5aa1ff;
31+
}
32+
}
33+
34+
* {
35+
box-sizing: border-box;
36+
}
37+
38+
html, body {
39+
height: 100%;
40+
}
941
body {
1042
font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, 'Helvetica Neue', Arial, 'Noto Sans', 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
11-
margin: 2rem;
43+
margin: 0;
44+
background: radial-gradient(1200px 800px at 100% 0%, rgba(79, 140, 255, 0.12), transparent 60%),
45+
radial-gradient(1000px 700px at 0% 100%, rgba(71, 182, 255, 0.12), transparent 60%),
46+
var(--bg);
47+
color: var(--text);
48+
-webkit-font-smoothing: antialiased;
49+
-moz-osx-font-smoothing: grayscale;
50+
}
51+
52+
.wrap {
53+
min-height: 100%;
54+
display: flex;
55+
align-items: center;
56+
justify-content: center;
57+
padding: 10vh 1rem 6vh;
1258
}
1359

1460
.card {
15-
border: 1px solid #e0e0e0;
16-
border-radius: 8px;
61+
border: 1px solid var(--border);
62+
border-radius: 16px;
1763
padding: 1.25rem;
64+
width: 100%;
1865
max-width: 720px;
19-
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06);
20-
margin: 0 auto;
21-
}
22-
23-
h1 {
24-
margin-top: 0;
25-
font-size: 1.5rem;
66+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.18);
67+
background: linear-gradient(180deg, rgba(255, 255, 255, 0.02), rgba(255, 255, 255, 0)), var(--panel);
68+
backdrop-filter: blur(6px);
2669
}
2770

28-
.muted {
29-
color: #666;
30-
}
71+
h1 {
72+
margin: 0 0 0.25rem 0;
73+
font-size: 1.5rem;
74+
letter-spacing: .2px;
75+
}
3176

32-
code {
33-
background: #f6f8fa;
34-
padding: 0.15rem 0.35rem;
35-
border-radius: 4px;
36-
}
77+
.muted {
78+
color: var(--muted);
79+
}
3780

38-
.form-row {
39-
display: flex;
40-
flex-direction: column;
41-
gap: 0.5rem;
42-
margin-top: 1rem;
43-
}
81+
.form-row {
82+
display: flex;
83+
flex-direction: column;
84+
gap: 0.75rem;
85+
margin-top: 1rem;
86+
}
4487

45-
label {
46-
font-weight: 600;
47-
}
88+
label {
89+
font-weight: 600;
90+
}
4891

4992
input[type="text"] {
50-
padding: 0.5rem;
93+
padding: 0.9rem 0.9rem;
5194
font-size: 1rem;
52-
border: 1px solid #ccc;
53-
border-radius: 6px;
95+
border: 1px solid var(--border);
96+
border-radius: 12px;
97+
background: color-mix(in oklab, var(--panel), #ffffff 3%);
98+
color: var(--text);
99+
outline: none;
54100
}
55101

102+
input[type="text"]:focus {
103+
border-color: var(--focus);
104+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--focus), transparent 80%);
105+
}
106+
107+
.row-inline {
108+
display: flex;
109+
gap: 0.5rem;
110+
align-items: stretch;
111+
}
112+
56113
button {
57-
padding: 0.5rem 1rem;
114+
padding: 0.9rem 1rem;
58115
font-size: 1rem;
59-
border: 1px solid #0d6efd;
60-
background: #0d6efd;
61-
color: white;
62-
border-radius: 6px;
116+
border: 1px solid var(--primary);
117+
background: var(--primary);
118+
color: var(--primary-contrast);
119+
border-radius: 12px;
63120
cursor: pointer;
121+
min-height: 44px;
64122
}
65123

66-
button:disabled {
67-
opacity: 0.6;
68-
cursor: not-allowed;
69-
}
70-
71-
.hint {
72-
color: #666;
73-
font-size: 0.9rem;
74-
}
75-
76-
@media (max-width: 600px) {
77-
body {
78-
margin: 0.5rem;
124+
button.secondary {
125+
background: transparent;
126+
color: var(--primary);
127+
border-color: var(--border);
79128
}
80129

81-
.card {
82-
max-width: 100%;
83-
padding: 0.75rem;
84-
box-sizing: border-box;
130+
button:disabled {
131+
opacity: 0.6;
132+
cursor: not-allowed;
85133
}
86134

87-
h1 {
88-
font-size: 1.1rem;
135+
.hint {
136+
color: var(--muted);
137+
font-size: 0.95rem;
89138
}
90139

91-
input[type="text"],
92-
button {
93-
font-size: 0.95rem;
140+
footer {
141+
text-align: center;
142+
margin-top: 1rem;
143+
font-size: .9rem;
144+
color: var(--muted);
94145
}
95146

96-
.form-row {
97-
gap: 0.35rem;
98-
}
147+
@media (max-width: 600px) {
148+
.wrap {
149+
padding: 6vh 0.75rem;
150+
}
99151

100-
.hint {
101-
font-size: 0.85rem;
102-
}
152+
h1 {
153+
font-size: 1.25rem;
154+
}
155+
156+
.row-inline {
157+
flex-wrap: wrap;
158+
}
159+
160+
.row-inline > * {
161+
flex: 1 1 100%;
162+
}
163+
164+
button {
165+
width: 100%;
166+
}
103167
}
104168
</style>
105169
</head>
106170

107171
<body>
108-
<div class="card">
109-
<h1 th:text="${message}">Welcome to Weather App!</h1>
110-
<hr />
111-
<form id="location-form" class="form-row" method="get" action="/report">
112-
<label for="city-input">Your town/city</label>
113-
<div style="display: flex; gap: 0.5rem; align-items: center;">
114-
<input id="city-input" name="city" type="text" list="city-suggestions" placeholder="Start typing your town..."
115-
autocomplete="off" style="flex: 1 1 auto;" />
116-
<button type="button" id="geo-btn" title="Use my location" style="white-space: nowrap;">Use my location</button>
117-
<button type="button" id="reset-btn" style="display:none;">Reset</button>
118-
</div>
119-
<input type="hidden" id="lat" name="lat" />
120-
<input type="hidden" id="lon" name="lon" />
121-
<datalist id="city-suggestions"></datalist>
122-
<div class="hint">Type more than 3 characters. Select an option like
123-
"City CC (lat,lon)" to enable submit.</div>
124-
<button id="submit-btn" type="submit" disabled>Submit</button>
125-
</form>
172+
<div class="wrap">
173+
<div class="card">
174+
<h1 th:text="${message}">Welcome to Weather App!</h1>
175+
<p class="muted">Find your location to view a rich, mobile-friendly weather report.</p>
176+
<form id="location-form" class="form-row" method="get" action="/report" novalidate>
177+
<label for="city-input">Your town or city</label>
178+
<div class="row-inline">
179+
<input id="city-input" name="city" type="text" list="city-suggestions"
180+
placeholder="Start typing your town..."
181+
autocomplete="off"/>
182+
<button type="button" id="geo-btn" title="Use my location">Use my location</button>
183+
<button type="button" class="secondary" id="reset-btn" style="display:none;">Reset</button>
184+
</div>
185+
<input type="hidden" id="lat" name="lat"/>
186+
<input type="hidden" id="lon" name="lon"/>
187+
<datalist id="city-suggestions"></datalist>
188+
<div class="hint">Type at least 4 characters or pick from suggestions like
189+
"City CC (lat,lon)". You can also use your current location.
190+
</div>
191+
<button id="submit-btn" type="submit" disabled>View report</button>
192+
</form>
193+
<footer>
194+
Powered by Spring Boot & Leaflet · AI summaries via Gemini when configured
195+
</footer>
196+
</div>
126197
</div>
127198

128199
<script>

0 commit comments

Comments
 (0)