De la Hugo la Jekyll
- 6 minute de citit
- 6 exemple de cod în Ruby, CSS și JavaScript
Disponibil și în English
Pe această pagină
Salut tuturor! 👋
O mică actualizare pentru blog: l-am mutat de la Hugo1 la Jekyll2.
Introducere
Hugo este un generator de site-uri statice, la fel ca Jekyll, este scris în Go și este destul de rapid. Este distribuit și ca un binar.
Totuși, asta nu m-a oprit să mă întorc la Jekyll, un generator de site-uri statice mai vechi, scris în Ruby.
Motivul principal al actualizării este că îmi place tema Minimal Mistakes3 scrisă de Michael Rose, îmi place ecosistemul Ruby (bundler, rake) și atât.
Iată ce nu mi-a plăcut la Hugo.
Nu mă interesează viteza, deoarece pentru blogul meu Jekyll construiește în aproximativ 10s, iar Hugo este de 10 ori mai rapid. În ceea ce privește distribuția binară, a trebuit mereu să instalez o mulțime de dependințe ca să construiesc versiunea hugo-extended, pentru că cel mai probabil nu era împachetată pentru distribuția mea Linux.
Uneori, când actualizam fie Hugo, fie tema, primeam erori de build și trebuia să sincronizez versiunea temei cu versiunea Hugo, iar uneori asta îmi strica modificările.
Nu-mi plăcea nici că trebuia să specific un URL de imagine în loc de un URL de fișier când foloseam o imagine hero sub tema Blowfish[4].
Ce îmi plăcea la Hugo sunt performanța, faptul că este scris în Go și capacitățile lui de internaționalizare.
Jekyll nu este perfect, dar funcționează bine pentru cazul meu. Îmi place și că am un Rakefile4 pe care îl folosesc
ca să construiesc blogul sub Linux și Windows și să-l încarc pe serverul meu prin rsync5:
desc "Serve the site locally"task :serve do sh "bundle exec jekyll serve --livereload"enddesc "Build and deploy the site via rsync"task :deploy => :build do is_windows = Gem.win_platform? sync_destination = "[email protected]:/blog" if is_windows sh "wsl rsync -arv -e 'ssh -i ~/.ssh/nuculabs.dev' ./_site/ #{sync_destination}" else sh "rsync -arv -e ssh ./_site/ #{sync_destination}" endendPersonalizări
Am început să fac câteva personalizări ale blogului ca să arate mai bine. Sunt mici îmbunătățiri pentru cuprins, pentru imaginea hero și pentru subsol.
Hero-ul paginii, ToC și subsolul
Poți folosi fișierul meu custom.sass:
.footer-flex display: flex justify-content: space-between// Modificări ale hero-ului paginii.page__hero position: absolute .page__hero-image opacity: 0.85 width: 100vw &::after content: "" position: absolute top: 0 left: 0 width: 100% height: 100% // Centrul este transparent (0), // Marginile sunt albe (1) background: linear-gradient(to bottom, transparent 70%, rgba(255,255,255,1) 100%), radial-gradient(ellipse 50% 100% at center, rgba(255,255,255,0.8) 0%, rgba(255,255,255,0.8) 45%, rgba(255,255,255,0) 100%) pointer-events: none// Îmbunătățire ToC.toc:not(:has(> ul)) display: none.btn--mastodon background-color: #6147e6 color: #fff &:visited, &:hover, &:focus, &:active background-color: #6147e6 color: #fff.btn--reddit background-color: #ff4500 color: #fff &:visited, &:hover, &:focus, &:active background-color: #ff4500 color: #fffNu uita să o imporți în fișierul scss principal.
@import "custom"; // stiluri personalizate _sass/custom.sassPartajare socială
Am modernizat și butoanele de partajare socială, pentru că îmi place să am Mastodon și Bluesky ca opțiuni.
Iată conținutul fișierului _includes/social-share.html.
<section class="page__share"> <h4 class="page__share-title">Distribuie pe</h4> <a href="https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fnuculabs.dev%2Fro%2Fweb%2520development%2F2026%2F02%2F24%2Ffrom-hugo-to-jekyll.html" class="btn btn--facebook" aria-label="Share on Facebook" onclick="window.open(this.href, 'window', 'left=20,top=20,width=500,height=500,toolbar=1,resizable=0'); return false;" title="Distribuie pe Facebook"> <i class="fab fa-fw fa-facebook" aria-hidden="true"></i><span> Facebook</span> </a> <a href="https://www.linkedin.com/shareArticle?mini=true&url=https://nuculabs.dev/ro/web%20development/2026/02/24/from-hugo-to-jekyll.html" class="btn btn--linkedin" aria-label="Share on LinkedIn" onclick="window.open(this.href, 'window', 'left=20,top=20,width=500,height=500,toolbar=1,resizable=0'); return false;" title="Distribuie pe LinkedIn"> <i class="fab fa-fw fa-linkedin" aria-hidden="true"></i><span> LinkedIn</span> </a> <a href="https://bsky.app/intent/compose?text=De+la+Hugo+la+Jekyll%20https%3A%2F%2Fnuculabs.dev%2Fro%2Fweb%2520development%2F2026%2F02%2F24%2Ffrom-hugo-to-jekyll.html" class="btn btn--bluesky" onclick="window.open(this.href, 'window', 'left=20,top=20,width=500,height=500,toolbar=1,resizable=0'); return false;" title="Distribuie pe Bluesky"> <i class="fab fa-fw fa-bluesky" aria-hidden="true"></i><span> Bluesky</span> </a> <a href="https://s2f.kytta.dev/?text=De+la+Hugo+la+Jekyll%20https%3A%2F%2Fnuculabs.dev%2Fro%2Fweb%2520development%2F2026%2F02%2F24%2Ffrom-hugo-to-jekyll.html" class="btn btn--mastodon" onclick=" window.open( this.href, 'window', 'left=20,top=20,width=500,height=500,toolbar=1,resizable=0', ); return false; " title="Distribuie pe Mastodon" > <i class="fab fa-fw fa-mastodon" aria-hidden="true"></i ><span> Mastodon</span> </a> <a href="https://www.reddit.com/submit/?url=https%3A%2F%2Fnuculabs.dev%2Fro%2Fweb%2520development%2F2026%2F02%2F24%2Ffrom-hugo-to-jekyll.html&resubmit=true&title=De+la+Hugo+la+Jekyll" class="btn btn--reddit" onclick=" window.open( this.href, 'window', 'left=20,top=20,width=500,height=500,toolbar=1,resizable=0', ); return false; " title="Distribuie pe Reddit" > <i class="fab fa-fw fa-reddit" aria-hidden="true"></i ><span> Reddit</span> </a></section>Lightbox
Un lightbox este util dacă afișezi imagini pe blog. Următorul JS și CSS fac ca, atunci când dai click pe o imagine, aceasta să fie redimensionată, iar fundalul să devină mai întunecat. Nu am scris scriptul original, dar l-am adaptat ca să funcționeze cu tema Minimal Mistakes.
Folosesc CSS simplu:
#lightbox {width: 100%; height: 100%; position: fixed; top: 0; left: 0; background: rgba(0,0,0,0.85); z-index: 9999999; line-height: 0; cursor: pointer; display: none;}#lightbox .img { position: relative; top: 50%; left: 50%; -ms-transform: translateX(-50%) translateY(-50%); -webkit-transform: translate(-50%,-50%); transform: translate(-50%,-50%); max-width: 100%; max-height: 100%;}#lightbox .img img {opacity: 0; pointer-events: none; width: auto;}@media screen and (min-width: 1200px) { #lightbox .img { max-width: 1200px; }}@media screen and (min-height: 1200px) { #lightbox .img { max-height: 1200px; }}#lightbox span {display: block; position: fixed; bottom: 13px; height: 1.5em; line-height: 1.4em; width: 100%; text-align: center; color: white; text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;}#lightbox span {display: none;}#lightbox .videoWrapperContainer { position: relative; top: 50%; left: 50%; -ms-transform: translateX(-50%) translateY(-50%); -webkit-transform: translate(-50%,-50%); transform: translate(-50%,-50%); max-width: 900px; max-height: 100%;}#lightbox .videoWrapperContainer .videoWrapper { height: 0; line-height: 0; margin: 0; padding: 0; position: relative; padding-bottom: 56.333%; /* personalizat */ background: black;}#lightbox .videoWrapper iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0; display: block;}#lightbox #prev, #lightbox #next {height: 50px; line-height: 36px; display: none; margin-top: -25px; position: fixed; top: 50%; padding: 0 15px; cursor: pointer; text-decoration: none; z-index: 99; color: white; font-size: 60px;}#lightbox.gallery #prev, #lightbox.gallery #next {display: block;}#lightbox #prev {left: 0;}#lightbox #next {right: 0;}#lightbox #close {height: 50px; width: 50px; position: fixed; cursor: pointer; text-decoration: none; z-index: 99; right: 0; top: 0;}#lightbox #close:after, #lightbox #close:before {position: absolute; margin-top: 22px; margin-left: 14px; content: ""; height: 3px; background: white; width: 23px; -webkit-transform-origin: 50% 50%; -moz-transform-origin: 50% 50%; -o-transform-origin: 50% 50%; transform-origin: 50% 50%; /* Safari */ -webkit-transform: rotate(-45deg); /* Firefox */ -moz-transform: rotate(-45deg); /* IE */ -ms-transform: rotate(-45deg); /* Opera */ -o-transform: rotate(-45deg);}#lightbox #close:after { /* Safari */ -webkit-transform: rotate(45deg); /* Firefox */ -moz-transform: rotate(45deg); /* IE */ -ms-transform: rotate(45deg); /* Opera */ -o-transform: rotate(45deg);}#lightbox, #lightbox * { -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none;}Și JavaScript simplu:
function is_youtubelink(url) { var p = /^(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/; return (url.match(p)) ? RegExp.$1 : false;}function is_imagelink(url) { var p = /([a-z\-_0-9\/\:\.]*\.(jpg|jpeg|png|gif))/i; return (url.match(p)) ? true : false;}function is_vimeolink(url,el) { var id = false; var xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == XMLHttpRequest.DONE) { // XMLHttpRequest.DONE == 4 if (xmlhttp.status == 200) { var response = JSON.parse(xmlhttp.responseText); id = response.video_id; console.log(id); el.classList.add('lightbox-vimeo'); el.setAttribute('data-id',id); el.addEventListener("click", function(event) { event.preventDefault(); document.getElementById('lightbox').innerHTML = '<a id="close"></a><a id="next">›</a><a id="prev">‹</a><div class="videoWrapperContainer"><div class="videoWrapper"><iframe src="https://player.vimeo.com/video/'+el.getAttribute('data-id')+'/?autoplay=1&byline=0&title=0&portrait=0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe></div></div>'; document.getElementById('lightbox').style.display = 'block'; setGallery(this); }); } else if (xmlhttp.status == 400) { alert('There was an error 400'); } else { alert('something else other than 200 was returned'); } } }; xmlhttp.open("GET", 'https://vimeo.com/api/oembed.json?url='+url, true); xmlhttp.send();}function setGallery(el) { var elements = document.body.querySelectorAll(".gallery"); elements.forEach(element => { element.classList.remove('gallery'); }); if(el.closest('ul, p')) { var link_elements = el.closest('ul, p').querySelectorAll("a[class*='lightbox-']"); link_elements.forEach(link_element => { link_element.classList.remove('current'); }); link_elements.forEach(link_element => { if(el.getAttribute('href') == link_element.getAttribute('href')) { link_element.classList.add('current'); } }); if(link_elements.length>1) { document.getElementById('lightbox').classList.add('gallery'); link_elements.forEach(link_element => { link_element.classList.add('gallery'); }); } var currentkey; var gallery_elements = document.querySelectorAll('a.gallery'); Object.keys(gallery_elements).forEach(function (k) { if(gallery_elements[k].classList.contains('current')) currentkey = k; }); if(currentkey==(gallery_elements.length-1)) var nextkey = 0; else var nextkey = parseInt(currentkey)+1; if(currentkey==0) var prevkey = parseInt(gallery_elements.length-1); else var prevkey = parseInt(currentkey)-1; document.getElementById('next').addEventListener("click", function() { gallery_elements[nextkey].click(); }); document.getElementById('prev').addEventListener("click", function() { gallery_elements[prevkey].click(); }); }}document.addEventListener("DOMContentLoaded", function() { //creează un div pentru lightbox în subsol var newdiv = document.createElement("div"); newdiv.setAttribute('id',"lightbox"); document.body.appendChild(newdiv); //adaugă clase linkurilor, ca să poată porni lightbox-urile var elements = document.querySelectorAll('.page__content a'); elements.forEach(element => { var url = element.getAttribute('href'); if(url) { if(url.indexOf('vimeo') !== -1 && !element.classList.contains('no-lightbox')) { is_vimeolink(url,element); } if(is_youtubelink(url) && !element.classList.contains('no-lightbox')) { element.classList.add('lightbox-youtube'); element.setAttribute('data-id',is_youtubelink(url)); } if(is_imagelink(url) && !element.classList.contains('no-lightbox')) { element.classList.add('lightbox-image'); var href = element.getAttribute('href'); var filename = href.split('/').pop(); var split = filename.split("."); var name = split[0]; element.setAttribute('title',name); } } var imageElements = document.querySelectorAll(".page__content img"); imageElements.forEach( element => { element.classList.add('lightbox-image'); }) }); //elimină lightbox-ul pe care s-a dat click document.getElementById('lightbox').addEventListener("click", function(event) { if(event.target.id != 'next' && event.target.id != 'prev'){ this.innerHTML = ''; document.getElementById('lightbox').style.display = 'none'; } }); //adaugă lightbox-ul YouTube la click var elements = document.querySelectorAll('a.lightbox-youtube'); elements.forEach(element => { element.addEventListener("click", function(event) { event.preventDefault(); document.getElementById('lightbox').innerHTML = '<a id="close"></a><a id="next">›</a><a id="prev">‹</a><div class="videoWrapperContainer"><div class="videoWrapper"><iframe src="https://www.youtube.com/embed/'+this.getAttribute('data-id')+'?autoplay=1&showinfo=0&rel=0"></iframe></div>'; document.getElementById('lightbox').style.display = 'block'; setGallery(this); }); }); //adaugă lightbox-ul de imagine la click var elements = document.querySelectorAll('a.lightbox-image'); elements.forEach(element => { element.addEventListener("click", function(event) { event.preventDefault(); document.getElementById('lightbox').innerHTML = '<a id="close"></a><a id="next">›</a><a id="prev">‹</a><div class="img" style="background: url(\''+this.getAttribute('href')+'\') center center / contain no-repeat;" title="'+this.getAttribute('title')+'" ><img src="'+this.getAttribute('href')+'" alt="'+this.getAttribute('title')+'" /></div><span>'+this.getAttribute('title')+'</span>'; document.getElementById('lightbox').style.display = 'block'; setGallery(this); }); }); var elements = document.querySelectorAll('img.lightbox-image'); elements.forEach(element => { element.addEventListener("click", function(event) { event.preventDefault(); document.getElementById('lightbox').innerHTML = '<a id="close"></a><a id="next">›</a><a id="prev">‹</a><div class="img" style="background: url(\''+this.getAttribute('src')+'\') center center / contain no-repeat;" title="'+this.getAttribute('title')+'" ><img src="'+this.getAttribute('src')+'" alt="'+this.getAttribute('title')+'" /></div><span>'+this.getAttribute('title')+'</span>'; document.getElementById('lightbox').style.display = 'block'; setGallery(this); }); });});Concluzie
Hugo este mai rapid și mai nou decât Jekyll, dar dacă îți place ecosistemul Ruby, poți încerca Jekyll. Tema Minimal Mistakes a lui Michael Rose tot este de top!