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.5 rem 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 {
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 : 5 px ;
114- right : 20 px ;
115- padding : .5 em .75 em .625 em .7 em ;
114+ top : 8 px ;
115+ right : 8 px ;
116+ padding : .35 em .6 em ;
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 >
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 & Free</ span >
183+ </ div >
184+ < p class ="text-body-secondary small mb-0 ">
185+ Convert any file to a base64 string instantly — 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+ · 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 —
256+ < a href ="https://swimburger.net "> swimburger.net</ a > ·
257+ < a href ="https://twitter.com/RealSwimburger "> X (Twitter)</ a > · ;
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