Skip to content

Commit 38ef3be

Browse files
committed
Add Open Graph and Twitter meta tags, enhance styles, and implement OG image generator
1 parent 213e0f6 commit 38ef3be

3 files changed

Lines changed: 184 additions & 44 deletions

File tree

index.html

Lines changed: 74 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@
99
<meta name="description"
1010
content="Use the form by selecting any file and the base64 string will be generated and placed below for easy copy/pasting. The files are not uploaded to any server. The encoding happens in the browser on your machine." />
1111
<meta property="og:title" content="Online base64 image encoder" />
12+
<meta property="og:image" content="https://img2base64.swimburger.net/og-image.png" />
1213
<meta name="twitter:title" content="Online base64 image encoder" />
14+
<meta name="twitter:image" content="https://img2base64.swimburger.net/og-image.png" />
1315
<meta property="og:description"
1416
content="Use the form by selecting any file and the base64 string will be generated and placed below for easy copy/pasting. The files are not uploaded to any server. The encoding happens in the browser on your machine." />
1517
<meta name="twitter:description"
1618
content="Use the form by selecting any file and the base64 string will be generated and placed below for easy copy/pasting. The files are not uploaded to any server. The encoding happens in the browser on your machine." />
17-
<meta name="twitter:card" content="summary" />
19+
<meta name="twitter:card" content="summary_large_image" />
1820
<meta name="twitter:site" content="@RealSwimburger" />
1921
<meta name="twitter:creator" content="@RealSwimburger" />
2022
<link rel="icon" type="image/png" href="https://swimburger.net/content/favicon/favicon-16x16.png" sizes="16x16" />
@@ -56,6 +58,37 @@
5658
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet"
5759
integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
5860
<style>
61+
/* Brand */
62+
:root {
63+
--brand-darkest: #4a0636;
64+
--brand-dark: #6c0a51;
65+
--brand-mid: #a02c56;
66+
--brand-accent: #ffc966;
67+
--brand-gradient: linear-gradient(135deg, #4a0636, #a02c56);
68+
--bs-body-font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
69+
--bs-primary: #a02c56;
70+
--bs-primary-rgb: 160, 44, 86;
71+
--bs-link-color: #a02c56;
72+
--bs-link-color-rgb: 160, 44, 86;
73+
--bs-link-hover-color: #6c0a51;
74+
}
75+
76+
[data-bs-theme="light"] {
77+
--bs-body-bg: #fafafa;
78+
}
79+
80+
/* Primary button */
81+
.btn-primary {
82+
--bs-btn-bg: var(--brand-mid);
83+
--bs-btn-border-color: var(--brand-mid);
84+
--bs-btn-hover-bg: var(--brand-dark);
85+
--bs-btn-hover-border-color: var(--brand-dark);
86+
--bs-btn-active-bg: var(--brand-darkest);
87+
--bs-btn-active-border-color: var(--brand-darkest);
88+
--bs-btn-focus-shadow-rgb: 160, 44, 86;
89+
}
90+
91+
/* GitHub corner */
5992
.github-corner:hover .octo-arm {
6093
animation: octocat-wave 560ms ease-in-out;
6194
}
@@ -73,34 +106,43 @@
73106

74107
/* Header */
75108
.site-header {
76-
background-color: var(--bs-body-bg);
77-
border-bottom: 1px solid var(--bs-border-color);
109+
background: var(--brand-gradient);
110+
border: none;
111+
}
112+
113+
.site-header h1,
114+
.site-header p {
115+
color: #fff;
116+
}
117+
118+
.site-header p {
119+
opacity: .85;
120+
}
121+
122+
.site-header .badge {
123+
background-color: rgba(255, 255, 255, 0.18) !important;
124+
color: var(--brand-accent);
78125
}
79126

80127
/* Drop zone */
81128
.drop-zone {
82129
border: 2px dashed var(--bs-border-color);
83130
border-radius: var(--bs-border-radius-lg);
84-
padding: 2.5rem 2rem;
131+
padding: 3rem 2rem;
85132
text-align: center;
86133
cursor: pointer;
87-
transition: background-color 0.15s ease, border-color 0.15s ease;
134+
transition: background-color 0.2s ease, border-color 0.2s ease;
88135
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);
95136
}
96137

138+
.drop-zone:hover,
97139
.drop-zone.drag-over {
98-
background-color: var(--bs-primary-bg-subtle);
99-
border-color: var(--bs-primary);
140+
border-color: var(--brand-mid);
141+
background-color: rgba(160, 44, 86, 0.03);
100142
}
101143

102144
.drop-zone:focus {
103-
outline: 2px solid var(--bs-primary);
145+
outline: 2px solid var(--brand-mid);
104146
outline-offset: 2px;
105147
}
106148

@@ -119,21 +161,17 @@
119161
}
120162

121163
#output-text {
122-
font-family: var(--bs-font-monospace);
164+
font-family: 'Cascadia Code', ui-monospace, 'JetBrains Mono', 'Fira Code', Consolas, monospace;
123165
font-size: .8rem;
124166
resize: vertical;
125167
}
126168

127169
/* Image preview */
128170
.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);
132171
min-height: 200px;
133172
display: flex;
134173
align-items: center;
135174
justify-content: center;
136-
overflow: hidden;
137175
}
138176

139177
.preview-card img {
@@ -143,21 +181,21 @@
143181

144182
/* Footer */
145183
footer {
146-
border-top: 1px solid var(--bs-border-color);
147-
color: var(--bs-secondary-color);
184+
background: var(--brand-gradient);
185+
color: rgba(255, 255, 255, 0.8);
148186
font-size: .875rem;
149187
}
150188

151189
footer a {
152-
color: inherit;
190+
color: var(--brand-accent);
153191
text-decoration: underline;
154192
}
155193

156194
footer img {
157195
float: left;
158196
margin-right: 6px;
159197
margin-top: 1px;
160-
opacity: .7;
198+
opacity: .8;
161199
}
162200

163201
/* Snippet generator */
@@ -166,10 +204,8 @@
166204
}
167205

168206
.snippet-input {
169-
font-family: var(--bs-font-monospace);
207+
font-family: 'Cascadia Code', ui-monospace, 'JetBrains Mono', 'Fira Code', Consolas, monospace;
170208
font-size: .75rem;
171-
color: var(--bs-secondary-color);
172-
background-color: var(--bs-tertiary-bg);
173209
cursor: default;
174210
}
175211
</style>
@@ -178,7 +214,7 @@
178214
<body class="d-flex flex-column min-vh-100">
179215
<a href="https://github.com/Swimburger/ImageToBase64String" class="github-corner"
180216
aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250"
181-
style="fill:#151513; color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true">
217+
style="fill:#4a0636; color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true">
182218
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
183219
<path
184220
d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
@@ -190,18 +226,15 @@
190226

191227
<header class="site-header py-3 mb-2">
192228
<div class="container">
193-
<div class="d-flex align-items-center gap-2 mb-1">
194-
<h1 class="h4 fw-semibold mb-0">Base64 Image Encoder</h1>
195-
<span class="badge text-bg-secondary fw-normal">Online &amp; Free</span>
196-
</div>
197-
<p class="text-body-secondary small mb-0">
198-
Convert any file to a base64 string instantly &mdash; runs entirely in your browser, nothing is uploaded.
229+
<h1 class="h5 fw-semibold mb-1">Base64 Image Encoder</h1>
230+
<p class="small mb-0">
231+
Convert any file to base64 &mdash; entirely in your browser, nothing uploaded.
199232
</p>
200233
</div>
201234
</header>
202235

203-
<main class="container flex-grow-1 py-3">
204-
<div class="mb-4">
236+
<main class="container flex-grow-1 py-5">
237+
<div class="mb-5">
205238
<div id="drop-zone" class="drop-zone" role="button" tabindex="0"
206239
aria-label="Drop file here, click to browse, or paste from clipboard">
207240
<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" fill="currentColor"
@@ -212,18 +245,18 @@ <h1 class="h4 fw-semibold mb-0">Base64 Image Encoder</h1>
212245
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"/>
213246
</svg>
214247
<p class="fw-medium mb-1">Drop a file here</p>
215-
<p class="text-body-secondary small mb-0">
216-
or <span class="text-primary fw-medium">click to browse</span>
248+
<p class="small mb-0">
249+
or <span style="color:var(--brand-mid)" class="fw-medium">click to browse</span>
217250
&nbsp;&middot;&nbsp; paste with <kbd>Ctrl+V</kbd>
218251
</p>
219252
<input id="file-input" type="file" class="visually-hidden" tabindex="-1">
220253
</div>
221254
</div>
222255

223-
<div class="row g-4">
256+
<div class="row g-5">
224257
<div class="col-lg-6">
225258
<div class="d-flex justify-content-between align-items-center mb-2">
226-
<label for="output-text" class="form-label mb-0 fw-medium">Base64 output</label>
259+
<label for="output-text" class="form-label mb-0 text-body-secondary">Base64 output</label>
227260
<div class="btn-group btn-group-sm" role="group" aria-label="Output format">
228261
<input type="radio" class="btn-check" name="format" id="format-dataurl" value="dataurl" checked>
229262
<label class="btn btn-outline-secondary py-0 px-2" style="font-size:.75rem;" for="format-dataurl">Data URL</label>
@@ -245,7 +278,6 @@ <h1 class="h4 fw-semibold mb-0">Base64 Image Encoder</h1>
245278
</div>
246279

247280
<div id="snippet-section" class="snippet-section mt-3">
248-
<p class="form-label fw-medium mb-2">Use as</p>
249281
<div class="d-flex flex-column gap-2">
250282
<div class="input-group input-group-sm">
251283
<span class="input-group-text text-body-secondary" style="width:5rem;">HTML</span>
@@ -266,13 +298,11 @@ <h1 class="h4 fw-semibold mb-0">Base64 Image Encoder</h1>
266298
</div>
267299
</div>
268300
<div class="col-lg-6">
269-
<p class="form-label fw-medium mb-2">Image preview</p>
301+
270302
<div class="preview-card p-3">
271303
<img id="output-image" class="img-fluid rounded" loading="lazy" decoding="async"
272304
src="" alt="" style="display:none;">
273-
<p id="preview-placeholder" class="text-body-secondary small mb-0">
274-
Preview will appear here after selecting an image file.
275-
</p>
305+
<p id="preview-placeholder" class="text-body-secondary small mb-0">Image preview</p>
276306
</div>
277307
</div>
278308
</div>

og-generator.html

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>OG Image Generator</title>
6+
<style>
7+
body {
8+
margin: 0;
9+
background: #111;
10+
display: flex;
11+
flex-direction: column;
12+
align-items: center;
13+
padding: 2rem;
14+
gap: 1.5rem;
15+
font-family: 'Segoe UI', sans-serif;
16+
}
17+
canvas {
18+
max-width: 100%;
19+
border-radius: 8px;
20+
box-shadow: 0 8px 40px rgba(0,0,0,.6);
21+
}
22+
button {
23+
padding: .75rem 2rem;
24+
background: #a02c56;
25+
color: #fff;
26+
border: none;
27+
border-radius: 6px;
28+
font-size: 1rem;
29+
cursor: pointer;
30+
}
31+
button:hover { background: #6c0a51; }
32+
</style>
33+
</head>
34+
<body>
35+
<canvas id="og" width="1200" height="630"></canvas>
36+
<button onclick="download()">Download og-image.png</button>
37+
<script>
38+
const canvas = document.getElementById('og');
39+
const ctx = canvas.getContext('2d');
40+
const W = 1200, H = 630, PAD = 96;
41+
42+
function roundRect(x, y, w, h, r) {
43+
ctx.beginPath();
44+
ctx.moveTo(x + r, y);
45+
ctx.lineTo(x + w - r, y);
46+
ctx.arcTo(x + w, y, x + w, y + r, r);
47+
ctx.lineTo(x + w, y + h - r);
48+
ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
49+
ctx.lineTo(x + r, y + h);
50+
ctx.arcTo(x, y + h, x, y + h - r, r);
51+
ctx.lineTo(x, y + r);
52+
ctx.arcTo(x, y, x + r, y, r);
53+
ctx.closePath();
54+
}
55+
56+
// Background gradient
57+
const grad = ctx.createLinearGradient(0, 0, W, H);
58+
grad.addColorStop(0, '#4a0636');
59+
grad.addColorStop(1, '#a02c56');
60+
ctx.fillStyle = grad;
61+
ctx.fillRect(0, 0, W, H);
62+
63+
// Dot grid texture
64+
ctx.fillStyle = 'rgba(255,255,255,0.045)';
65+
for (let x = 48; x < W; x += 40)
66+
for (let y = 40; y < H; y += 40) {
67+
ctx.beginPath();
68+
ctx.arc(x, y, 1.5, 0, Math.PI * 2);
69+
ctx.fill();
70+
}
71+
72+
// Title
73+
ctx.fillStyle = '#fff';
74+
ctx.font = 'bold 62px "Segoe UI", Tahoma, Arial, sans-serif';
75+
ctx.fillText('Base64 Image Encoder', PAD, 210);
76+
77+
// Subtitle
78+
ctx.fillStyle = 'rgba(255,255,255,0.7)';
79+
ctx.font = '27px "Segoe UI", Tahoma, Arial, sans-serif';
80+
ctx.fillText('Convert any image to base64 — instantly in your browser', PAD, 268);
81+
82+
// Code box
83+
const boxY = 316, boxH = 72, boxW = W - PAD * 2;
84+
ctx.fillStyle = 'rgba(13,17,23,0.6)';
85+
roundRect(PAD, boxY, boxW, boxH, 10);
86+
ctx.fill();
87+
88+
// Code text: accent prefix + muted data
89+
const mono = '22px "Cascadia Code", "Fira Code", Consolas, monospace';
90+
ctx.font = mono;
91+
const prefix = 'data:image/jpeg;base64,';
92+
ctx.fillStyle = '#ffc966';
93+
ctx.fillText(prefix, PAD + 22, boxY + 45);
94+
ctx.fillStyle = 'rgba(230,237,243,0.55)';
95+
ctx.fillText('/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgICAgJCAkKCgkN…', PAD + 22 + ctx.measureText(prefix).width, boxY + 45);
96+
97+
// Brand
98+
ctx.fillStyle = '#ffc966';
99+
ctx.font = '22px "Segoe UI", Tahoma, Arial, sans-serif';
100+
ctx.fillText('swimburger.net', PAD, 568);
101+
102+
function download() {
103+
const a = document.createElement('a');
104+
a.download = 'og-image.png';
105+
a.href = canvas.toDataURL('image/png');
106+
a.click();
107+
}
108+
</script>
109+
</body>
110+
</html>

og-image.png

90 KB
Loading

0 commit comments

Comments
 (0)