jekyll-theme-consulting 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +53 -0
- data/_includes/contact.html +4 -3
- data/_includes/contact_form.html +11 -9
- data/_includes/contact_script.html +64 -23
- data/_sass/libs/_vars.scss +47 -45
- data/_sass/main.scss +1 -0
- data/_sass/main/components/_modal.css +50 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1303e48e96de64a397286ba1e967c908766059f74f63441b9558de6cad51e640
|
4
|
+
data.tar.gz: faea6567ccf1c4c9296b879be97cb3571896c0beef9e013b6585f0928e3827da
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 47a0d039df89f13940c29499c8490a5eb68586c29d38923a06e376ee44b4c2c5b551455ea2e031a0faf81c1458f884ceabad8ac12abf9f89163447ed7e51711a
|
7
|
+
data.tar.gz: daec8ff000a41334f72e321615945b1375007062e107f58cd01eeec71df8e8d78bf76636fce3b08e8a4b6491b4e588102267f96cca990210ac05b5d963274ac7
|
data/README.md
CHANGED
@@ -24,6 +24,8 @@ You can preview the theme [here](https://moodule.github.io/jekyll-theme-consulti
|
|
24
24
|
|
25
25
|
> **Light**: svg images, thumbs and lazy loading for raster images, few libraries
|
26
26
|
|
27
|
+
> **Performant**: parallel loading & execution of assets, support lazy-loading, use webp image format, optimal critical path
|
28
|
+
|
27
29
|
> **Hardened**: form validation, recaptcha, verified libraries
|
28
30
|
|
29
31
|
# Installation
|
@@ -80,6 +82,57 @@ The contact form is validated using [google's recaptcha plugin][recaptcha-docume
|
|
80
82
|
First, you need to [sign your website up] to enable the plugin.
|
81
83
|
Google will provide you with a **client-side integration key**: copy it to `_config.yml` under `recaptcha.sitekey`.
|
82
84
|
|
85
|
+
## Images
|
86
|
+
|
87
|
+
### Lazy-Loading
|
88
|
+
|
89
|
+
Images downloading & rendering can be defered until they enter the viewport.
|
90
|
+
This shortens the loading of the webpage, with no consequence on the displayed content since the images are off-screen.
|
91
|
+
|
92
|
+
There are 2 possibilities:
|
93
|
+
1) using the scripts bundled with the gem
|
94
|
+
2) depending on the browser
|
95
|
+
|
96
|
+
Browser support for lazy-loading is varying, so it is recommanded to opt for the first solution.
|
97
|
+
|
98
|
+
#### Using the gem functionality
|
99
|
+
|
100
|
+
- add `class="lazy-loading"`
|
101
|
+
- fill the attribute `src` with the path to the placeholder file
|
102
|
+
- fill the attribute `data-src` with the path to the content file
|
103
|
+
|
104
|
+
Before:
|
105
|
+
```html
|
106
|
+
<img src="{{ 'assets/images/content.jpg' | absolute_url }}" />
|
107
|
+
```
|
108
|
+
|
109
|
+
After:
|
110
|
+
```html
|
111
|
+
<img class="lazy-loading" src="{{ 'assets/images/placeholder.jpg' | absolute_url }}" data-src="{{ 'assets/images/content.jpg' | absolute_url }}" />
|
112
|
+
```
|
113
|
+
|
114
|
+
#### Using the browser functionality
|
115
|
+
|
116
|
+
- add `loading="lazy"`
|
117
|
+
|
118
|
+
Before:
|
119
|
+
```html
|
120
|
+
<img src="{{ 'assets/images/content.jpg' | absolute_url }}" />
|
121
|
+
```
|
122
|
+
|
123
|
+
After:
|
124
|
+
```html
|
125
|
+
<img loading="lazy" src="{{ 'assets/images/content.jpg' | absolute_url }}" />
|
126
|
+
```
|
127
|
+
|
128
|
+
> This functionality is not supported by all the browsers!
|
129
|
+
|
130
|
+
### Compression and file formats
|
131
|
+
|
132
|
+
It is recommanded to use the `webp` format.
|
133
|
+
|
134
|
+
At a given dimension and compression level (quality) the webp files are at least half the size with a smoother feel.
|
135
|
+
|
83
136
|
## Publication
|
84
137
|
|
85
138
|
### On Github Pages
|
data/_includes/contact.html
CHANGED
@@ -5,15 +5,16 @@
|
|
5
5
|
{%- assign submit_input_id="contact-submit-button" -%}
|
6
6
|
{%- assign reset_input_id="contact-reset-button" -%}
|
7
7
|
{%- assign recaptcha_widget_id="recaptcha-checkbox" -%}
|
8
|
-
{%- assign onsubmit_callback="
|
8
|
+
{%- assign onsubmit_callback="processContactRequest" -%}
|
9
9
|
{%- assign onreset_callback="resetContactForm" -%}
|
10
|
+
{%- assign onfocusout_callback="toggleSubmitInputElement" -%}
|
10
11
|
<section id="contact" class="inverted" style="{{ include.style | default: nil }}">
|
11
12
|
<header class="major">
|
12
13
|
<h2>Contact Me</h2>
|
13
14
|
</header>
|
14
15
|
<div class="row gtr-200">
|
15
16
|
<div class="col-6 col-12-medium col-12-small col-12-xsmall">
|
16
|
-
{% include contact_form.html form_id=form_id name_input_id=name_input_id email_input_id=email_input_id message_input_id=message_input_id submit_input_id=submit_input_id reset_input_id=reset_input_id recaptcha_widget_id=recaptcha_widget_id onsubmit_callback=onsubmit_callback onreset_callback=onreset_callback %}
|
17
|
+
{% include contact_form.html form_id=form_id name_input_id=name_input_id email_input_id=email_input_id message_input_id=message_input_id submit_input_id=submit_input_id reset_input_id=reset_input_id recaptcha_widget_id=recaptcha_widget_id onsubmit_callback=onsubmit_callback onreset_callback=onreset_callback onfocusout_callback=onfocusout_callback %}
|
17
18
|
</div>
|
18
19
|
<div class="col-6 col-12-medium col-12-small col-12-xsmall">
|
19
20
|
<ul class="contact">
|
@@ -26,4 +27,4 @@
|
|
26
27
|
</div>
|
27
28
|
</section>
|
28
29
|
|
29
|
-
{% include contact_script.html form_id=form_id name_input_id=name_input_id email_input_id=email_input_id message_input_id=message_input_id submit_input_id=submit_input_id reset_input_id=reset_input_id recaptcha_widget_id=recaptcha_widget_id onsubmit_callback=onsubmit_callback onreset_callback=onreset_callback %}
|
30
|
+
{% include contact_script.html form_id=form_id name_input_id=name_input_id email_input_id=email_input_id message_input_id=message_input_id submit_input_id=submit_input_id reset_input_id=reset_input_id recaptcha_widget_id=recaptcha_widget_id onsubmit_callback=onsubmit_callback onreset_callback=onreset_callback onfocusout_callback=onfocusout_callback %}
|
data/_includes/contact_form.html
CHANGED
@@ -1,24 +1,26 @@
|
|
1
|
-
<form id="{{ include.form_id | 'contact-form' }}" method="post" action="
|
1
|
+
<form id="{{ include.form_id | default: 'contact-form' }}" method="post" action="javascript:void(0);" onsubmit="{{ include.onsubmit_callback | default: 'processContactRequest' }}()" onreset="{{ include.onreset_callback | default: 'resetContactForm' }}()" >
|
2
2
|
<div class="row gtr-uniform">
|
3
3
|
<div class="col-6 col-12-small col-12-xsmall">
|
4
|
-
<input type="text" name="{{ include.name_input_id | 'contact-name' }}" id="{{ include.name_input_id | 'contact-name' }}" value="" placeholder="Name" required minlength="4" maxlength="64" />
|
4
|
+
<input type="text" name="{{ include.name_input_id | default: 'contact-name' }}" id="{{ include.name_input_id | default: 'contact-name' }}" value="" placeholder="Name" required minlength="4" maxlength="64" onfocusout="{{ include.onfocusout_callback | default: 'toggleSubmitInputElement' }}()" />
|
5
5
|
</div>
|
6
6
|
<div class="col-6 col-12-small col-12-xsmall">
|
7
|
-
<input type="email" name="{{ include.email_input_id | 'contact-email' }}" id="{{ include.email_input_id | 'contact-email' }}" value="" placeholder="Email" required minlength="8" maxlength="64" />
|
7
|
+
<input type="email" name="{{ include.email_input_id | default: 'contact-email' }}" id="{{ include.email_input_id | default: 'contact-email' }}" value="" placeholder="Email" required minlength="8" maxlength="64" onfocusout="{{ include.onfocusout_callback | default: 'toggleSubmitInputElement' }}()" />
|
8
8
|
</div>
|
9
9
|
<!-- Break -->
|
10
10
|
<div class="col-12">
|
11
|
-
<textarea name="{{ include.message_input_id | 'contact-message' }}" id="{{ include.message_input_id | 'contact-message' }}" placeholder="Enter your message" rows="6" required minlength="16" maxlength="256" >Please get back to me!</textarea>
|
11
|
+
<textarea name="{{ include.message_input_id | default: 'contact-message' }}" id="{{ include.message_input_id | default: 'contact-message' }}" placeholder="Enter your message" rows="6" required minlength="16" maxlength="256" onfocusout="{{ include.onfocusout_callback | default: 'toggleSubmitInputElement' }}()" >Please get back to me!</textarea>
|
12
12
|
</div>
|
13
13
|
<!-- Break -->
|
14
14
|
<div class="col-12">
|
15
15
|
<ul class="actions">
|
16
|
-
<li><input type="submit" id="{{ include.submit_input_id | 'contact-submit-button' }}" value="Send Message" class="primary button solid fa-envelope" disabled/></li>
|
17
|
-
<li><input type="reset" id="{{ include.reset_input_id | 'contact-reset-button' }}" value="Reset" class="icon button fa-undo" /></li>
|
18
|
-
</ul>
|
19
|
-
<ul class="actions">
|
20
|
-
<li><div id="{{ include.recaptcha_widget_id | 'recaptcha-checkbox' }}" ></div></li>
|
16
|
+
<li><input type="submit" id="{{ include.submit_input_id | default: 'contact-submit-button' }}" value="Send Message" class="primary button solid fa-envelope" disabled/></li>
|
17
|
+
<li><input type="reset" id="{{ include.reset_input_id | default: 'contact-reset-button' }}" value="Reset" class="icon button fa-undo" /></li>
|
21
18
|
</ul>
|
22
19
|
</div>
|
23
20
|
</div>
|
21
|
+
<div id="modal-recaptcha-window" class="modal" tabIndex="-1">
|
22
|
+
<div class="inner">
|
23
|
+
<div id="{{ include.recaptcha_widget_id | default: 'recaptcha-checkbox' }}" ></div>
|
24
|
+
</div>
|
25
|
+
</div>
|
24
26
|
</form>
|
@@ -4,6 +4,7 @@
|
|
4
4
|
const emailInputElement = document.getElementById("{{ include.email_input_id | default: 'contact-email' }}");
|
5
5
|
const messageInputElement = document.getElementById("{{ include.message_input_id | default: 'contact-message' }}");
|
6
6
|
const submitInputElement = document.getElementById("{{ include.submit_input_id | default: 'contact-submit-button' }}");
|
7
|
+
const modalWindowElement = document.getElementById("{{ include.modal_window_id | default: 'modal-recaptcha-window' }}");
|
7
8
|
var recaptchaWidget;
|
8
9
|
|
9
10
|
const isInputValid = function() {
|
@@ -11,42 +12,82 @@
|
|
11
12
|
&& emailInputElement.checkValidity()
|
12
13
|
&& messageInputElement.checkValidity())
|
13
14
|
};
|
14
|
-
|
15
|
-
const isRecaptchaValid = function() {
|
16
|
-
var responseToken = grecaptcha.getResponse(recaptchaWidget);
|
17
|
-
return true;
|
18
|
-
};
|
19
15
|
|
20
16
|
const disableSubmitInputElement = function (disabled = true) {
|
21
17
|
submitInputElement.disabled = disabled;
|
22
18
|
}
|
23
19
|
|
24
|
-
const {{ include.
|
20
|
+
const {{ include.onfocusout_callback | default: 'toggleSubmitInputElement' }} = function() {
|
21
|
+
if (isInputValid()) {
|
22
|
+
disableSubmitInputElement(false);
|
23
|
+
} else {
|
24
|
+
disableSubmitInputElement();
|
25
|
+
}
|
26
|
+
};
|
27
|
+
|
28
|
+
const showModalWindow = function() {
|
29
|
+
modalWindowElement.classList.add('visible');
|
30
|
+
modalWindowElement.classList.add('loaded');
|
31
|
+
modalWindowElement.focus();
|
32
|
+
}
|
33
|
+
|
34
|
+
const hideModalWindow = function() {
|
35
|
+
modalWindowElement.classList.remove('loaded');
|
36
|
+
modalWindowElement.classList.remove('visible');
|
37
|
+
contactFormElement.focus();
|
38
|
+
}
|
39
|
+
|
40
|
+
const {{ include.onsubmit_callback | default: 'processContactRequest()' }} = function() {
|
41
|
+
if (grecaptcha.getResponse(recaptchaWidget).length > 0) {
|
42
|
+
sendContactMessage();
|
43
|
+
} else {
|
44
|
+
showModalWindow();
|
45
|
+
}
|
46
|
+
};
|
47
|
+
|
48
|
+
const {{ include.onreset_callback | default: 'resetContactForm' }} = function() {
|
25
49
|
contactFormElement.reset();
|
26
50
|
disableSubmitInputElement();
|
27
51
|
};
|
28
52
|
|
29
|
-
const
|
30
|
-
|
31
|
-
|
32
|
-
|
53
|
+
const sendContactMessage = function() {
|
54
|
+
const recaptchaToken = grecaptcha.getResponse(recaptchaWidget);
|
55
|
+
|
56
|
+
const data = {
|
57
|
+
name: encodeURIComponent(nameInputElement.value),
|
58
|
+
email: encodeURIComponent(emailInputElement.value),
|
59
|
+
message: encodeURIComponent(messageInputElement),
|
60
|
+
token: recaptchaToken
|
61
|
+
};
|
62
|
+
|
63
|
+
hideModalWindow();
|
64
|
+
|
65
|
+
if (recaptchaToken.length > 0) {
|
66
|
+
fetch("{{ '/contact' | absolute_url }}", {
|
67
|
+
method: 'POST',
|
68
|
+
mode: 'same-origin',
|
69
|
+
headers: {'Content-Type': 'application/json',},
|
70
|
+
body: JSON.stringify(data),
|
71
|
+
})
|
72
|
+
.then((response) => {
|
73
|
+
alert('Message sent!');
|
74
|
+
})
|
75
|
+
.catch((error) => {
|
76
|
+
console.error('Error:', error);
|
77
|
+
});
|
78
|
+
}
|
33
79
|
};
|
34
80
|
|
35
|
-
var
|
36
|
-
|
37
|
-
if (isInputValid() && isRecaptchaValid()) {
|
38
|
-
disableSubmitInputElement(false);
|
39
|
-
} else {
|
40
|
-
disableSubmitInputElement();
|
41
|
-
}
|
42
|
-
};
|
43
|
-
recaptchaWidget = grecaptcha.render('{{ include.recaptcha_widget_id | 'recaptcha-checkbox' }}', {
|
81
|
+
var onRecaptchaLoadCallback = function() {
|
82
|
+
recaptchaWidget = grecaptcha.render("{{ include.recaptcha_widget_id | default: 'recaptcha-checkbox' }}", {
|
44
83
|
'sitekey' : '{{ site.recaptcha.sitekey }}',
|
45
84
|
'theme' : 'dark',
|
46
|
-
'
|
47
|
-
'
|
48
|
-
'size' : 'compact'
|
85
|
+
'size' : 'compact',
|
86
|
+
'callback' : sendContactMessage
|
49
87
|
});
|
50
88
|
};
|
89
|
+
|
90
|
+
modalWindowElement.addEventListener('click', hideModalWindow);
|
91
|
+
modalWindowElement.addEventListener('keyup', hideModalWindow);
|
51
92
|
</script>
|
52
|
-
<script src="https://www.google.com/recaptcha/api.js?onload=
|
93
|
+
<script src="https://www.google.com/recaptcha/api.js?onload=onRecaptchaLoadCallback&render=explicit" async defer></script>
|
data/_sass/libs/_vars.scss
CHANGED
@@ -1,54 +1,56 @@
|
|
1
1
|
// Misc.
|
2
|
-
|
3
|
-
|
4
|
-
|
2
|
+
$misc: (
|
3
|
+
z-index-base: 10000,
|
4
|
+
lightbox-opacity: 0.875
|
5
|
+
);
|
5
6
|
|
6
7
|
// Duration.
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
$duration: (
|
9
|
+
nav: 0.5s,
|
10
|
+
transition: 0.2s,
|
11
|
+
lightbox-fadein: 0.5s
|
12
|
+
);
|
11
13
|
|
12
14
|
// Size.
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
15
|
+
$size: (
|
16
|
+
border-radius: 0.375em,
|
17
|
+
element-height: 2.75em,
|
18
|
+
element-margin: 2em,
|
19
|
+
sidebar-width: 26em,
|
20
|
+
sidebar-width-alt: 24em,
|
21
|
+
gutter: 3em
|
22
|
+
);
|
21
23
|
|
22
24
|
// Font.
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
25
|
+
$font: (
|
26
|
+
family: ('Open Sans', sans-serif),
|
27
|
+
family-heading: ('Roboto Slab', serif),
|
28
|
+
family-fixed: ('Courier New', monospace),
|
29
|
+
weight: 400,
|
30
|
+
weight-bold: 600,
|
31
|
+
weight-heading: 700,
|
32
|
+
weight-heading-alt: 400,
|
33
|
+
kerning-heading: 0.075em
|
34
|
+
);
|
33
35
|
|
34
36
|
// Palette.
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
37
|
+
$palette: (
|
38
|
+
bg: #ffffff,
|
39
|
+
bg-alt: #f5f6f7,
|
40
|
+
bg-inverted: #6666ff,
|
41
|
+
bg-alt-inverted: #223344,
|
42
|
+
fg: #000000,
|
43
|
+
fg-bold: #3d4449,
|
44
|
+
fg-light: #9fa3a6,
|
45
|
+
fg-inverted: #ffffff,
|
46
|
+
fg-bold-inverted: #ffffff,
|
47
|
+
fg-light-inverted: #ffffff,
|
48
|
+
border: rgba(210,215,217,0.75),
|
49
|
+
border-bg: transparentize(#e6ebed, 0.75),
|
50
|
+
border-inverted: #ffffff,
|
51
|
+
border-bg-inverted: #ffffff,
|
52
|
+
accent: #6666ff,
|
53
|
+
accent-cp: #ffdd44,
|
54
|
+
accent-inverted: #ffffff,
|
55
|
+
accent-cp-inverted: #6666ff,
|
56
|
+
);
|
data/_sass/main.scss
CHANGED
@@ -0,0 +1,50 @@
|
|
1
|
+
.modal {
|
2
|
+
@include vendor('display', 'flex');
|
3
|
+
@include vendor('align-items', 'center');
|
4
|
+
@include vendor('justify-content', 'center');
|
5
|
+
@include vendor('pointer-events', 'none');
|
6
|
+
@include vendor('user-select', 'none');
|
7
|
+
@include vendor('transition', (
|
8
|
+
'opacity #{_duration(lightbox-fadein)} ease',
|
9
|
+
'visibility #{_duration(lightbox-fadein)}',
|
10
|
+
'z-index #{_duration(lightbox-fadein)}'
|
11
|
+
));
|
12
|
+
position: fixed;
|
13
|
+
top: 0;
|
14
|
+
left: 0;
|
15
|
+
width: 100%;
|
16
|
+
height: 100%;
|
17
|
+
opacity: 0;
|
18
|
+
outline: 0;
|
19
|
+
visibility: none;
|
20
|
+
z-index: 0;
|
21
|
+
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
22
|
+
background-color: transparentize(_palette(bg), 1 - _misc(lightbox-opacity));
|
23
|
+
|
24
|
+
.inner {
|
25
|
+
@include vendor('transform', 'translateY(0.75rem)');
|
26
|
+
@include vendor('transition', (
|
27
|
+
'opacity #{_duration(lightbox-fadein) * 0.5} ease',
|
28
|
+
'transform #{_duration(lightbox-fadein) * 0.5} ease'
|
29
|
+
));
|
30
|
+
opacity: 0;
|
31
|
+
}
|
32
|
+
|
33
|
+
&.visible {
|
34
|
+
@include vendor('pointer-events', 'auto');
|
35
|
+
opacity: 1;
|
36
|
+
visibility: visible;
|
37
|
+
z-index: _misc(z-index-base) + 1000;
|
38
|
+
}
|
39
|
+
|
40
|
+
&.loaded {
|
41
|
+
.inner {
|
42
|
+
@include vendor('transform', 'translateY(0)');
|
43
|
+
@include vendor('transition', (
|
44
|
+
'opacity #{_duration(lightbox-fadein)} ease',
|
45
|
+
'transform #{_duration(lightbox-fadein)} ease'
|
46
|
+
));
|
47
|
+
opacity: 1;
|
48
|
+
}
|
49
|
+
}
|
50
|
+
}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jekyll-theme-consulting
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Mougeolle
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-01-
|
11
|
+
date: 2020-01-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jekyll
|
@@ -153,6 +153,7 @@ files:
|
|
153
153
|
- _sass/main/components/_image.scss
|
154
154
|
- _sass/main/components/_list.scss
|
155
155
|
- _sass/main/components/_mini-posts.scss
|
156
|
+
- _sass/main/components/_modal.css
|
156
157
|
- _sass/main/components/_pagination.scss
|
157
158
|
- _sass/main/components/_posts.scss
|
158
159
|
- _sass/main/components/_row.scss
|