Skip to content

Commit 10f6698

Browse files
committed
Enhance UI: header bar, styled drop zone, preview card, monospace textarea, cleaner footer
1 parent b31d132 commit 10f6698

File tree

1 file changed

+131
-76
lines changed

1 file changed

+131
-76
lines changed

index.html

Lines changed: 131 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -71,27 +71,27 @@
7171
.github-corner .octo-arm { animation: octocat-wave 560ms ease-in-out; }
7272
}
7373

74-
footer {
75-
background-color: var(--bs-tertiary-bg);
76-
}
77-
78-
footer a {
79-
text-decoration: underline;
80-
}
81-
82-
footer img {
83-
float: left;
84-
margin-right: 5px;
74+
/* Header */
75+
.site-header {
76+
background-color: var(--bs-body-bg);
77+
border-bottom: 1px solid var(--bs-border-color);
8578
}
8679

80+
/* Drop zone */
8781
.drop-zone {
8882
border: 2px dashed var(--bs-border-color);
89-
border-radius: var(--bs-border-radius);
90-
padding: 2rem;
83+
border-radius: var(--bs-border-radius-lg);
84+
padding: 2.5rem 2rem;
9185
text-align: center;
9286
cursor: pointer;
9387
transition: background-color 0.15s ease, border-color 0.15s ease;
9488
user-select: none;
89+
background-color: var(--bs-tertiary-bg);
90+
}
91+
92+
.drop-zone:hover {
93+
border-color: var(--bs-primary);
94+
background-color: var(--bs-primary-bg-subtle);
9595
}
9696

9797
.drop-zone.drag-over {
@@ -104,16 +104,60 @@
104104
outline-offset: 2px;
105105
}
106106

107+
/* Output textarea */
107108
.output-text-wrapper {
108109
position: relative;
109110
}
110111

111112
.output-text-wrapper button {
112113
position: absolute;
113-
top: 5px;
114-
right: 20px;
115-
padding: .5em .75em .625em .7em;
114+
top: 8px;
115+
right: 8px;
116+
padding: .35em .6em;
116117
line-height: 1;
118+
z-index: 1;
119+
}
120+
121+
#output-text {
122+
font-family: var(--bs-font-monospace);
123+
font-size: .8rem;
124+
resize: vertical;
125+
}
126+
127+
/* Image preview */
128+
.preview-card {
129+
border: 1px solid var(--bs-border-color);
130+
border-radius: var(--bs-border-radius-lg);
131+
background-color: var(--bs-tertiary-bg);
132+
min-height: 200px;
133+
display: flex;
134+
align-items: center;
135+
justify-content: center;
136+
overflow: hidden;
137+
}
138+
139+
.preview-card img {
140+
max-height: 480px;
141+
object-fit: contain;
142+
}
143+
144+
/* Footer */
145+
footer {
146+
border-top: 1px solid var(--bs-border-color);
147+
color: var(--bs-secondary-color);
148+
font-size: .875rem;
149+
}
150+
151+
footer a {
152+
color: inherit;
153+
text-decoration: underline;
154+
}
155+
156+
footer img {
157+
float: left;
158+
margin-right: 6px;
159+
margin-top: 1px;
160+
opacity: .7;
117161
}
118162
</style>
119163
</head>
@@ -130,83 +174,91 @@
130174
d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
131175
fill="currentColor" class="octo-body"></path>
132176
</svg></a>
133-
<main class="container flex-grow-1">
134-
<h1 class="display-4">Online base64 image encoder</h1>
135-
<p>
136-
Instead of referencing an image by its URL, you can also display images on web pages by embedding the base64
137-
encoded string as the src-attribute.
138-
This does make your HTML page heavier and the browser will not be able to cache the image as it normally
139-
would when referencing by URL.
140-
Though it's not recommended to embed images like this, there are some usecases where base64 encoded images
141-
are useful.
142-
</p>
143-
<p>
144-
<b>Drop a file, click to browse, or paste from clipboard (Ctrl+V).
145-
The files are not uploaded to any server. The encoding happens in the browser on your machine.</b>
146-
If this tool doesn't work, the file is probably too large.
147-
</p>
148-
<div class="mb-3">
149-
<label class="form-label">Select file</label>
177+
178+
<header class="site-header py-3 mb-2">
179+
<div class="container">
180+
<div class="d-flex align-items-center gap-2 mb-1">
181+
<h1 class="h4 fw-semibold mb-0">Base64 Image Encoder</h1>
182+
<span class="badge text-bg-secondary fw-normal">Online &amp; Free</span>
183+
</div>
184+
<p class="text-body-secondary small mb-0">
185+
Convert any file to a base64 string instantly &mdash; runs entirely in your browser, nothing is uploaded.
186+
</p>
187+
</div>
188+
</header>
189+
190+
<main class="container flex-grow-1 py-3">
191+
<div class="mb-4">
150192
<div id="drop-zone" class="drop-zone" role="button" tabindex="0"
151193
aria-label="Drop file here, click to browse, or paste from clipboard">
152-
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor"
153-
class="bi bi-upload mb-2 text-body-secondary" viewBox="0 0 16 16" aria-hidden="true">
154-
<path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/>
155-
<path d="M7.646 1.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 2.707V11.5a.5.5 0 0 1-1 0V2.707L5.354 4.854a.5.5 0 1 1-.708-.708l3-3z"/>
194+
<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" fill="currentColor"
195+
class="bi bi-cloud-arrow-up mb-3 text-primary" viewBox="0 0 16 16" aria-hidden="true">
196+
<path fill-rule="evenodd"
197+
d="M7.646 5.146a.5.5 0 0 1 .708 0l2 2a.5.5 0 0 1-.708.708L8.5 6.707V10.5a.5.5 0 0 1-1 0V6.707L6.354 7.854a.5.5 0 1 1-.708-.708l2-2z"/>
198+
<path
199+
d="M4.406 3.342A5.53 5.53 0 0 1 8 2c2.69 0 4.923 2 5.166 4.579C14.758 6.804 16 8.137 16 9.773 16 11.569 14.502 13 12.687 13H3.781C1.708 13 0 11.366 0 9.318c0-1.763 1.266-3.223 2.942-3.593.143-.863.698-1.723 1.464-2.383zm.653.757c-.757.653-1.153 1.44-1.153 2.056v.448l-.445.049C2.064 6.805 1 7.952 1 9.318 1 10.785 2.23 12 3.781 12h8.906C13.98 12 15 10.988 15 9.773c0-1.216-1.02-2.228-2.313-2.228h-.5v-.5C12.188 4.825 10.328 3 8 3a4.53 4.53 0 0 0-2.941 1.1z"/>
156200
</svg>
157-
<div>Drop file here, <span class="text-primary">click to browse</span>, or paste from clipboard</div>
201+
<p class="fw-medium mb-1">Drop a file here</p>
202+
<p class="text-body-secondary small mb-0">
203+
or <span class="text-primary fw-medium">click to browse</span>
204+
&nbsp;&middot;&nbsp; paste with <kbd>Ctrl+V</kbd>
205+
</p>
158206
<input id="file-input" type="file" class="visually-hidden" tabindex="-1">
159207
</div>
160208
</div>
161-
<div class="row">
162-
<div class="col-sm">
163-
<div class="mb-3">
164-
<div class="d-flex justify-content-between align-items-center mb-1">
165-
<label for="output-text" class="form-label mb-0">Base64 output</label>
166-
<div class="btn-group btn-group-sm" role="group" aria-label="Output format">
167-
<input type="radio" class="btn-check" name="format" id="format-dataurl" value="dataurl" checked>
168-
<label class="btn btn-outline-secondary py-0 px-2" style="font-size:.75rem;" for="format-dataurl">Data URL</label>
169-
<input type="radio" class="btn-check" name="format" id="format-base64" value="base64">
170-
<label class="btn btn-outline-secondary py-0 px-2" style="font-size:.75rem;" for="format-base64">Base64 only</label>
171-
</div>
172-
</div>
173-
<div class="output-text-wrapper">
174-
<button id="copy-button" type="button" class="btn btn-light" title="Copy to clipboard">
175-
<svg id="clipboard-icon" xmlns="http://www.w3.org/2000/svg" width="16" height="16"
176-
fill="currentColor" class="bi bi-clipboard" viewBox="0 0 16 16">
177-
<path
178-
d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1v-1z" />
179-
<path
180-
d="M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5h3zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3z" />
181-
</svg>
182-
<svg id="check-icon" style="display: none;" xmlns="http://www.w3.org/2000/svg" width="16"
183-
height="16" fill="currentColor" class="bi bi-check" viewBox="0 0 16 16">
184-
<path
185-
d="M10.97 4.97a.75.75 0 0 1 1.07 1.05l-3.99 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425a.267.267 0 0 1 .02-.022z" />
186-
</svg>
187-
</button>
188-
<textarea id="output-text" class="form-control" rows="20" readonly></textarea>
209+
210+
<div class="row g-4">
211+
<div class="col-lg-6">
212+
<div class="d-flex justify-content-between align-items-center mb-2">
213+
<label for="output-text" class="form-label mb-0 fw-medium">Base64 output</label>
214+
<div class="btn-group btn-group-sm" role="group" aria-label="Output format">
215+
<input type="radio" class="btn-check" name="format" id="format-dataurl" value="dataurl" checked>
216+
<label class="btn btn-outline-secondary py-0 px-2" style="font-size:.75rem;" for="format-dataurl">Data URL</label>
217+
<input type="radio" class="btn-check" name="format" id="format-base64" value="base64">
218+
<label class="btn btn-outline-secondary py-0 px-2" style="font-size:.75rem;" for="format-base64">Base64 only</label>
189219
</div>
190220
</div>
221+
<div class="output-text-wrapper">
222+
<button id="copy-button" type="button" class="btn btn-sm btn-light border" title="Copy to clipboard">
223+
<svg id="clipboard-icon" xmlns="http://www.w3.org/2000/svg" width="14" height="14"
224+
fill="currentColor" class="bi bi-clipboard" viewBox="0 0 16 16">
225+
<path
226+
d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1v-1z" />
227+
<path
228+
d="M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5h3zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3z" />
229+
</svg>
230+
<svg id="check-icon" style="display: none;" xmlns="http://www.w3.org/2000/svg" width="14"
231+
height="14" fill="currentColor" class="bi bi-check" viewBox="0 0 16 16">
232+
<path
233+
d="M10.97 4.97a.75.75 0 0 1 1.07 1.05l-3.99 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425a.267.267 0 0 1 .02-.022z" />
234+
</svg>
235+
</button>
236+
<textarea id="output-text" class="form-control" rows="20" readonly></textarea>
237+
</div>
191238
</div>
192-
<div class="col-sm">
193-
<div class="mb-3">
194-
<label class="form-label">Image preview</label>
195-
<img id="output-image" class="img-fluid" loading="lazy" decoding="async"
239+
<div class="col-lg-6">
240+
<p class="form-label fw-medium mb-2">Image preview</p>
241+
<div class="preview-card p-3">
242+
<img id="output-image" class="img-fluid rounded" loading="lazy" decoding="async"
196243
src="" alt="" style="display:none;">
244+
<p id="preview-placeholder" class="text-body-secondary small mb-0">
245+
Preview will appear here after selecting an image file.
246+
</p>
197247
</div>
198248
</div>
199249
</div>
200250
</main>
201-
<footer class="footer mt-auto py-3">
251+
252+
<footer class="mt-auto py-3">
202253
<div class="container">
203-
<img src="https://swimburger.net/content/img/burger.svg" width="40" height="22" alt="Swimburger logo">
204-
Tool provided by Niels Swimberghe; Follow Niels' blog at
205-
<a href="https://swimburger.net">swimburger.net</a> or on <a
206-
href="https://twitter.com/RealSwimburger">X (Twitter)</a>;
254+
<img src="https://swimburger.net/content/img/burger.svg" width="36" height="20" alt="Swimburger logo">
255+
Tool by Niels Swimberghe &mdash;
256+
<a href="https://swimburger.net">swimburger.net</a> &middot;
257+
<a href="https://twitter.com/RealSwimburger">X (Twitter)</a> &middot;
207258
<a href="https://privacypolicies.com/privacy/view/95b2095f4609044d9618c21b06f5df75">Privacy Policy</a>
208259
</div>
209260
</footer>
261+
210262
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"
211263
integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI"
212264
crossorigin="anonymous"></script>
@@ -215,6 +267,7 @@ <h1 class="display-4">Online base64 image encoder</h1>
215267
const fileInput = document.getElementById('file-input');
216268
const dropZone = document.getElementById('drop-zone');
217269
const outputImage = document.getElementById('output-image');
270+
const previewPlaceholder = document.getElementById('preview-placeholder');
218271
const outputText = document.getElementById('output-text');
219272
const copyButton = document.getElementById('copy-button');
220273
const clipboardIcon = document.getElementById('clipboard-icon');
@@ -241,9 +294,11 @@ <h1 class="display-4">Online base64 image encoder</h1>
241294
if (file.type?.startsWith('image/')) {
242295
outputImage.src = lastDataUrl;
243296
outputImage.style.display = 'block';
297+
previewPlaceholder.style.display = 'none';
244298
} else {
245299
outputImage.src = '';
246300
outputImage.style.display = 'none';
301+
previewPlaceholder.style.display = 'block';
247302
}
248303
};
249304
reader.readAsDataURL(file);
@@ -291,10 +346,10 @@ <h1 class="display-4">Online base64 image encoder</h1>
291346
if (file) processFile(file);
292347
});
293348

294-
// Format toggle
295349
// Load ramen image as default
296350
fetch('ramen.jpg').then(r => r.blob()).then(blob => processFile(blob));
297351

352+
// Format toggle
298353
document.querySelectorAll('input[name="format"]').forEach(
299354
input => input.addEventListener('change', updateOutput)
300355
);

0 commit comments

Comments
 (0)