README.md
@@ -129,7 +129,6 @@ [params] doNotLoadAnimations = true # Animations are loaded by default ``` ### Control the date Format You can change the default date formating for the `list.html`, the `single.html` and the `index.html`. Simply configure the matching parameters. ```toml @@ -267,9 +266,38 @@ Step 1: Configure the `contactFormAction` in the `config.toml` ```toml [params] #contactFormAction = "https://formspree.io/f/your-form-hash-here" contactFormAction = "https://formspree.io/f/your-form-hash-here" ``` Step 2: Activate the `contact: true` or `contact=true` in the frontmatter of a page. See `exampleSite/content/contact.html` as an example. Step 3: If you wish to use a Google reCAPTCHA v2, you must [go to a Google Admin console](https://www.google.com/recaptcha/about/) and create a reCAPTCHA: Make sure it is a reCAPTCHA v2. You must then [go to your Formspree account](https://help.formspree.io/hc/en-us/articles/360022811154-Adding-a-custom-reCAPTCHA-key) and enter the secret key that you were given in the appropriate location. Step 4: Enter the reCAPTCHA site key in `config.toml`: ```toml [params] contactFormReCaptchaSiteKey = "your-site-key" ``` ### Netlify Contact Form on the Contact Page Netlify forms only work when your site is being actively hosted by Netlify - unlike Formspree, testing your form locally before committing to Netlify will not work. Step 1: Configure the `contactFormType` in the `config.toml`. Netlify does not require a `contactFormAction` configured. ```toml [params] contactFormType = "netlify" ``` Step 2: Set `contact: true` or `contact=true` in the frontmatter of a page. See `exampleSite/content/contact.html` as an example. Step 3: If you wish to use a Google reCAPTCHA v2, you must [go to a Google Admin console](https://www.google.com/recaptcha/about/) and create a reCAPTCHA. Make sure it is a reCAPTCHA v2. You must then [go to your Formspree account](https://help.formspree.io/hc/en-us/articles/360022811154-Adding-a-custom-reCAPTCHA-key) and enter the secret key that you were given in the appropriate location. Step 4: Enter the reCAPTCHA site key in `config.toml`: ```toml [params] contactFormReCaptchaSiteKey = "your-site-key" ``` Step 5: [Go to your Netlify account](https://www.netlify.com/blog/2018/05/23/bring-your-own-recaptcha-to-netlify-forms/) and let them know both the site key and the secret key for your custom reCAPTCHA. Step 6: You will probably want to set up some [form notification](https://docs.netlify.com/forms/notifications/). ### Twitter Cards support In order to use the full functionality of Twitter cards, you will have to define a couple of settings in the `config.toml` and the frontmatter of a page. assets/css/style.css
@@ -14,8 +14,10 @@ --tag-color: #424242; --blockquote-text-color: #858585; --blockquote-border-color: #dfe2e5; --error-color: #ff3939; --thumbnail-height: 15em; scroll-padding-top: 100px; --font-family: 'Verdana', sans-serif; } html[data-theme='dark'] { @@ -32,6 +34,7 @@ --tag-color: rgb(191, 191, 191); --blockquote-text-color: #808080; --blockquote-border-color: #424242; --error-color: #ff3939; } html { @@ -41,7 +44,7 @@ body { color: var(--body-color); font-family: 'Verdana', sans-serif; font-family: var(--font-family); font-size: 15px; width: 100%; margin: 0 auto 30px auto; @@ -130,40 +133,48 @@ @-webkit-keyframes fadeInDown { 0% { opacity: 0; -webkit-transform: translateY(-20px); } 100% { opacity: 1; -webkit-transform: translateY(0); } } @-moz-keyframes fadeInDown { 0% { opacity: 0; -moz-transform: translateY(-20px); } 100% { opacity: 1; -moz-transform: translateY(0); } } @-o-keyframes fadeInDown { 0% { opacity: 0; -o-transform: translateY(-20px); } 100% { opacity: 1; -o-transform: translateY(0); } } @keyframes fadeInDown { 0% { opacity: 0; transform: translateY(-20px); } 100% { opacity: 1; transform: translateY(0); } } @@ -1068,6 +1079,10 @@ .form-style ul li input.align-right { float:right; } .form-style input, .form-style textarea { font-family: var(--font-family); } .form-style ul li textarea { background-color: var(--bg-color); border: 1px solid var(--form-border-color); @@ -1075,7 +1090,6 @@ width: 100%; height: auto; } .form-style ul li input[type="button"], .form-style ul li input[type="submit"] { background-color: var(--bg-color); border: 1px solid var(--form-border-color); @@ -1085,10 +1099,20 @@ text-decoration: none; width: 100%; } .form-style ul li input[type="button"]:hover, .form-style ul li input[type="submit"]:hover { background-color: var(--bg-color); border: 1px solid var(--form-button-hover-border-color); } .form-style .form-row { display: flex; flex-direction: row; align-items: center; flex: 1; } .form-feedback { text-align: center; } .form-feedback[feedback-success="false"] { color: var(--error-color); } /* (CONTACT) FORM END */ i18n/de.toml
@@ -20,5 +20,32 @@ [comments] other = "Kommentare" [name] other = "Name" [name_error] other = "Bitte geben Sie Ihren Namen an" [email] other = "Email" [email_error] other = "Bitte geben Sie eine gültige E-Mail Adresse ein" [message] other = "Nachricht" [message_error] other = "Bitte teilen Sie uns mit, warum Sie sich an uns wenden" [send] other = "Senden" [form_captcha_error] other = "Captcha-Fehler; bitte versuchen Sie es später noch einmal" [form_error] other = "Fataler Fehler, Kontakt nicht hergestellt - bitte melden Sie sich auf anderem Wege" [form_success] other = "Thank für Ihre Kontaktaufnahme. Wir werden uns bald bei Ihnen melden." i18n/dk.toml
@@ -20,5 +20,32 @@ [comments] other = "kommentar" [name] other = "Name" [name_error] other = "Please provide your name" [email] other = "Email" [email_error] other = "Please enter a valid email address" [message] other = "Message" [message_error] other = "Please let us know why you are contacting us" [send] other = "Sende" [form_captcha_error] other = "Captcha error; please try again later" [form_error] other = "Fatal error, contact not made - please reach out some other way" [form_success] other = "Thank you for reaching out! We will be touch soon" i18n/en.toml
@@ -20,5 +20,32 @@ [comments] other = "comments" [name] other = "Name" [name_error] other = "Please provide your name" [email] other = "Email" [email_error] other = "Please enter a valid email address" [message] other = "Message" [message_error] other = "Please let us know why you are contacting us" [send] other = "Send" [form_captcha_error] other = "Captcha error; please try again later" [form_error] other = "Fatal error, contact not made - please reach out in some other way" [form_success] other = "Thank you for reaching out! We will be touch soon" i18n/es.toml
@@ -20,5 +20,32 @@ [comments] other = "comentarios" [name] other = "Nombre" [name_error] other = "Por favor, indique su nombre" [email] other = "Email" [email_error] other = "Por favor, introduzca una dirección de correo electrónico válida" [message] other = "Mensaje" [message_error] other = "Por favor, háganos saber por qué está contactando con nosotros" [send] other = "Enviar" [form_captcha_error] other = "Error de Captcha; por favor, inténtelo de nuevo más tarde" [form_error] other = "Error fatal, contacto no realizado - por favor, contacte de otra manera" [form_success] other = "¡Gracias por tender la mano! Pronto nos tocará" i18n/fa.toml
@@ -20,5 +20,32 @@ [comments] other = "نظرات" [name] other = "Name" [name_error] other = "Please provide your name" [email] other = "Email" [email_error] other = "Please enter a valid email address" [message] other = "Message" [message_error] other = "Please let us know why you are contacting us" [send] other = "ارسال" [form_captcha_error] other = "Captcha error; please try again later" [form_error] other = "Fatal error, contact not made - please reach out some other way" [form_success] other = "Thank you for reaching out! We will be touch soon" i18n/fi.toml
@@ -20,5 +20,32 @@ [comments] other = "kommentit" [name] other = "Name" [name_error] other = "Please provide your name" [email] other = "Email" [email_error] other = "Please enter a valid email address" [message] other = "Message" [message_error] other = "Please let us know why you are contacting us" [send] other = "Lähetä" [form_captcha_error] other = "Captcha error; please try again later" [form_error] other = "Fatal error, contact not made - please reach out some other way" [form_success] other = "Thank you for reaching out! We will be touch soon" i18n/fr.toml
@@ -20,5 +20,32 @@ [comments] other = "commentaire" [name] other = "Nom" [name_error] other = "Veuillez indiquer votre nom" [email] other = "Email" [email_error] other = "Veuillez saisir une adresse électronique valide" [message] other = "Message" [message_error] other = "Veuillez nous indiquer la raison pour laquelle vous nous contactez" [send] other = "Envoyer" [form_captcha_error] other = "Erreur Captcha ; veuillez réessayer plus tard" [form_error] other = "Erreur fatale, contact non établi - veuillez prendre contact d'une autre manière" [form_success] other = "Merci de nous avoir contactés ! Nous vous contacterons bientôt" i18n/it.toml
@@ -20,5 +20,32 @@ [comments] other = "commenti" [name] other = "Nome" [name_error] other = "Si prega di fornire il proprio nome" [email] other = "e-mail" [email_error] other = "Inserire un indirizzo e-mail valido" [message] other = "Messaggio" [message_error] other = "Per favore, fateci sapere perché ci contattate" [send] other = "Spedire" [form_captcha_error] other = "Errore Captcha; si prega di riprovare più tardi" [form_error] other = "Errore fatale, contatto non effettuato - si prega di contattare in altro modo" [form_success] other = "Grazie per averci raggiunto! Ci sentiremo presto" i18n/pt-br.toml
@@ -20,5 +20,32 @@ [comments] other = "comentários" [name] other = "Nome" [name_error] other = "Por favor, forneça seu nome" [email] other = "Email" [email_error] other = "Por favor, digite um endereço de e-mail válido" [message] other = "Mensagem" [message_error] other = "Por favor nos informe por que você está entrando em contato conosco" [send] other = "Enviar" [form_captcha_error] other = "Captcha error; por favor, tente novamente mais tarde" [form_error] other = "Erro fatal, contato não feito - por favor, procure de outra forma" [form_success] other = "Obrigado por estender a mão! Em breve estaremos em contato" i18n/zh-cn.toml
@@ -20,5 +20,32 @@ [comments] other = "评论" [name] other = "名称" [name_error] other = "请提供您的姓名" [email] other = "电子邮件" [email_error] other = "请输入有效的电子邮件地址" [message] other = "留言内容" [message_error] other = "请告诉我们您联系我们的原因。" [send] other = "发送" [form_captcha_error] other = "验证码错误,请稍后再试" [form_error] other = "致命的错误,没有取得联系--请以其他方式联系。" [form_success] other = "谢谢您的联系!我们会尽快与您联系。我们会尽快联系您" i18n/zh-tw.toml
@@ -20,5 +20,32 @@ [comments] other = "註解" [name] other = "Name" [name_error] other = "Please provide your name" [email] other = "Email" [email_error] other = "Please enter a valid email address" [message] other = "Message" [message_error] other = "Please let us know why you are contacting us" [send] other = "發送" [form_captcha_error] other = "Captcha error; please try again later" [form_error] other = "Fatal error, contact not made - please reach out some other way" [form_success] other = "Thank you for reaching out! We will be touch soon" layouts/_default/list.html
@@ -1,5 +1,5 @@ {{ define "main" }} <div class="archive {{ with .Site.Params.doNotLoadAnimations }} . {{ else }} animated fadeInDown {{ end }}"> <div class="archive {{ if and (or (not (isset .Site.Params "doNotLoadAnimations")) (and (isset .Site.Params "doNotLoadAnimations") (not .Site.Params.doNotLoadAnimations))) (or (not (isset .Page.Params "animation")) .Page.Params.animation) }} animated fadeInDown {{ end }}"> <ul class="list-with-title"> {{ range .Data.Pages.GroupByDate "2006" }} <div class="listing-title">{{ .Key }}</div> layouts/_default/single.html
@@ -1,5 +1,5 @@ {{ define "main" }} <div class="post {{ with .Site.Params.doNotLoadAnimations }} . {{ else }} animated fadeInDown {{ end }}"> <div class="post {{ if and (or (not (isset .Site.Params "doNotLoadAnimations")) (and (isset .Site.Params "doNotLoadAnimations") (not .Site.Params.doNotLoadAnimations))) (or (not (isset .Page.Params "animation")) .Page.Params.animation) }} animated fadeInDown {{ end }}"> <div class="post-content"> {{ if .Params.thumbnail }} <img class="post-thumbnail" src="{{ .Params.thumbnail | absURL }}" alt="Thumbnail image"> layouts/index.html
@@ -1,6 +1,6 @@ {{ define "main" }} <div class="post {{ with .Site.Params.doNotLoadAnimations }} . {{ else }} animated fadeInDown {{ end }}"> <div class="post {{ if and (or (not (isset .Site.Params "doNotLoadAnimations")) (and (isset .Site.Params "doNotLoadAnimations") (not .Site.Params.doNotLoadAnimations))) (or (not (isset .Page.Params "animation")) .Page.Params.animation) }} animated fadeInDown {{ end }}"> <!-- (Optional) Home -- on top of `mainSections` content (aka posts) ; -- as declared in content/_index.md @@ -13,7 +13,7 @@ {{ $paginator := .Paginate (where .Site.RegularPages "Type" "in" .Site.Params.mainSections) }} {{ range $paginator.Pages }} <div class="post {{ with .Site.Params.doNotLoadAnimations }} . {{ else }} animated fadeInDown {{ end }}"> <div class="post {{ if and (or (not (isset .Site.Params "doNotLoadAnimations")) (and (isset .Site.Params "doNotLoadAnimations") (not .Site.Params.doNotLoadAnimations))) (or (not (isset .Page.Params "animation")) .Page.Params.animation) }} animated fadeInDown {{ end }}"> {{ if .Params.thumbnail }} <div class="post-thumbnail"> <a href="{{ .RelPermalink }}"> layouts/partials/contact.html
@@ -1,19 +1,115 @@ {{ $jquery := resources.Get "js/jquery.js" }} <script type="text/javascript" src="{{ $jquery.Permalink }}"></script> <div class="contact-form"> <form class="form-style" method="POST" action="{{ .Site.Params.contactFormAction }}" data-toggle="validator"> <form id="contact-form" name="Contact Form" class="form-style" method="POST" action="{{ if eq .Site.Params.contactFormType "netlify" }}/{{ else }}{{ .Site.Params.contactFormAction }}{{ end }}" {{ with .Site.Params.contactFormReCaptchaSiteKey }} data-netlify-recaptcha="true"{{ end }} {{ if eq .Site.Params.contactFormType "netlify" }} data-netlify="true" netlify-honeypot="my-lovely-house"{{ end }}> <ul> <li> <input class="field-style field-full" type="text" name="username" id="username" placeholder="Name" required> <input class="field-style field-full" type="text" name="username" id="username" placeholder="{{ i18n "name" }}" oninvalid="setCustomValidity('{{ i18n "name_error" }}')" oninput="setCustomValidity('')" required> </li> <li> <input class="field-style field-full" type="email" id="email" name="email" placeholder="Email" required> <input class="field-style field-full" type="email" name="email" id="email" placeholder="{{ i18n "email" }}" pattern="^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$" oninvalid="setCustomValidity('{{ i18n "email_error" }}')" oninput="setCustomValidity('')" required> </li> <li> <textarea class="field-style" name="message" id="message" rows="6" placeholder="{{ i18n "message" }}"></textarea> <textarea class="field-style" name="message" id="message" rows="6" placeholder="{{ i18n "message" }}" oninvalid="setCustomValidity('{{ i18n "message_error" }}')" oninput="setCustomValidity('')" required></textarea> </li> <li> <input class="field-style" type="submit" value="{{ i18n "send" }}"></input> <input class="field-style" id="form-submit" type="submit" value="{{ i18n "send" }}"> </li> <li> <div class="form-feedback" id="submit-feedback"></div> </li> {{ if eq .Site.Params.contactFormType "netlify"}} <input name="form-name" value="Contact Form" type="hidden"> <input class="field-style" name="my-lovely-house" id="my-lovely-house" type="hidden"> {{ end }} {{ with .Site.Params.contactFormReCaptchaSiteKey }} <li> <div id="g-recaptcha"></div> </li> {{ end }} </ul> </form> </div> <script> // Sets feedback message. function setFeedback(message = '', ok = true) { const feedback = $('#submit-feedback'); feedback.attr('feedback-success', ok); feedback.text(message); } // This hanlder takes care of submission post-captcha. function onSubmit() { // Verify if we have a recaptcha at all before checking. if($('.g-recaptcha')[0] != null && grecaptcha.getResponse().length == 0) { setFeedback('{{ i18n "form_captcha_error" }}', false); } else { // Formspree requires json; ajax will do the conversion. const form = $('#contact-form'); const message = form.serialize(); const dataType = ('{{ .Site.Params.ContactFormType }}' === 'netlify') ? 'application/x-www-form-urlencoded' : 'json'; $.ajax({ url: form.prop('action'), method: 'POST', data: message, dataType, complete: (xhr, status) => { // Netlify asks for urlencoded data but sends back HTML. This causes a // data type mismatch that Ajax flags as an error ... I believe the solution is to just // screen for the false negative in this combined handler. // https://stackoverflow.com/questions/16230624/ajax-call-fires-error-event-but-returns-200-ok/16230794 if (status === 'error' && xhr.status !== 200) { setFeedback('{{ i18n "form_error" }}', false); } else { setFeedback('{{ i18n "form_success" }}'); } }, }); // Enable button. $('#form-submit').prop('disabled', false); } } // Initial contact form submit is disabled, and optionally kicks off captcha. $('#contact-form').submit((e) => { // We're doing the submission to avoid the redirect for free Formspree accounts. e.preventDefault(); setFeedback(''); // Disable submit button for the time being, $('#form-submit').prop('disabled', true); // Make sure we have a captcha to execute before trying to do so. if ($('.g-recaptcha')[0] !== null) { grecaptcha.execute(); } else { onSubmit(); } }); </script> {{ with .Site.Params.contactFormReCaptchaSiteKey }} <script> // Captcha rendered dynamically to deal with some rendering issues with // theme and with animation. function captchaLoadCallback() { grecaptcha.render( 'g-recaptcha', { sitekey: '{{ . }}', callback: onSubmit, size: 'invisible', badge: 'bottomright', theme: $('html').attr('data-theme'), }); } </script> <!-- Explicit rendering of recaptcha to deal with data-theme and animation issues --> <script type="text/javascript" src="https://www.google.com/recaptcha/api.js?onload=captchaLoadCallback&render=explicit" async defer></script> {{ end }} layouts/partials/navbar.html
@@ -1,4 +1,4 @@ <div class="page-top {{ with .Site.Params.doNotLoadAnimations }} . {{ else }} animated fadeInDown {{ end }}"> <div class="page-top {{ if and (or (not (isset .Site.Params "doNotLoadAnimations")) (and (isset .Site.Params "doNotLoadAnimations") (not .Site.Params.doNotLoadAnimations))) (or (not (isset .Page.Params "animation")) .Page.Params.animation) }} animated fadeInDown {{ end }}"> <a role="button" class="navbar-burger" data-target="navMenu" aria-label="menu" aria-expanded="false"> <span aria-hidden="true"></span> <span aria-hidden="true"></span> layouts/partials/sidebar.html
@@ -1,4 +1,4 @@ <div class="sidebar{{ with .Site.Params.doNotLoadAnimations }} . {{ else }} animated fadeInDown {{ end }}"> <div class="sidebar{{ if and (or (not (isset .Site.Params "doNotLoadAnimations")) (and (isset .Site.Params "doNotLoadAnimations") (not .Site.Params.doNotLoadAnimations))) (or (not (isset .Page.Params "animation")) .Page.Params.animation) }} animated fadeInDown {{ end }}"> <div class="logo-title"> <div class="title"> <img src="{{ .Site.Params.profilePicture | absURL }}" alt="profile picture">