Compare commits
24 Commits
0089c93afa
...
feat/seo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98f639b913 | ||
|
|
40e9660d80 | ||
|
|
ef6876a0d3 | ||
|
|
851547b87d | ||
|
|
bd803eda00 | ||
|
|
ac76ba32f6 | ||
|
|
c84ca1d538 | ||
|
|
3cc9d535ff | ||
|
|
a24c2681db | ||
|
|
2bced48437 | ||
|
|
7adeb66c6c | ||
|
|
8ac4c6b675 | ||
|
|
e662c7cda4 | ||
|
|
78c41cc071 | ||
|
|
140552a35f | ||
|
|
dd3a0240f9 | ||
|
|
1ea47eb2da | ||
|
|
686ece4479 | ||
|
|
90340d03aa | ||
|
|
63869c3090 | ||
|
|
17fb355560 | ||
|
|
c7fafad8d4 | ||
|
|
69552e1fc5 | ||
|
|
67d08d9865 |
38
.dockerignore
Normal file
38
.dockerignore
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# Hugo build artifacts
|
||||||
|
.hugo_build.lock
|
||||||
|
hugo_stats.json
|
||||||
|
public/
|
||||||
|
resources/
|
||||||
|
|
||||||
|
# Node.js
|
||||||
|
node_modules/
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Development files
|
||||||
|
.editorconfig
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
*.md
|
||||||
|
docs/
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Backup files
|
||||||
|
*.backup
|
||||||
|
*.bak
|
||||||
|
*.tmp
|
||||||
|
|
||||||
|
# IDE files
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# Build artifacts
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
43
.editorconfig
Normal file
43
.editorconfig
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# EditorConfig is awesome: https://EditorConfig.org
|
||||||
|
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
# Unix-style newlines with a newline ending every file
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
# 4 space indentation (no tabs)
|
||||||
|
[*.{js,jsx,ts,tsx,html,css,scss,md}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
# Set charset
|
||||||
|
[*.{js,jsx,ts,tsx,html,css,scss,md}]
|
||||||
|
charset = utf-8
|
||||||
|
|
||||||
|
# For Go files
|
||||||
|
[*.go]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
# For Python files
|
||||||
|
[*.py]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
# For JSON files
|
||||||
|
[*.json]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
# For YAML files
|
||||||
|
[*.{yaml,yml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
# For TOML files
|
||||||
|
[*.toml]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
27
Dockerfile
Normal file
27
Dockerfile
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# Use a Node.js LTS Alpine image as the base
|
||||||
|
FROM hugomods/hugo:nightly
|
||||||
|
|
||||||
|
# Install Node.js and npm (needed for fetch-wordpress.js and sass)
|
||||||
|
RUN apk add --no-cache nodejs npm
|
||||||
|
|
||||||
|
# Install Sass (Dart Sass) globally
|
||||||
|
RUN npm install -g sass
|
||||||
|
|
||||||
|
# Set the working directory inside the container
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy the entire project into the container
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Make the build script executable (if not already)
|
||||||
|
RUN chmod +x scripts/build.sh
|
||||||
|
|
||||||
|
# Install Node.js dependencies if any (e.g., for fetch-wordpress.js)
|
||||||
|
# Assuming package.json exists and has dependencies
|
||||||
|
RUN if [ -f package.json ]; then npm install; fi
|
||||||
|
|
||||||
|
# Ensure /usr/local/bin is in PATH for the CMD
|
||||||
|
ENV PATH="/usr/local/bin:$PATH"
|
||||||
|
|
||||||
|
# Command to run the build script when the container starts
|
||||||
|
CMD ["./scripts/build.sh"]
|
||||||
@@ -20,6 +20,8 @@ h1,h2,h3,h4,h5,h6 {
|
|||||||
color: get-color("dark");
|
color: get-color("dark");
|
||||||
font-family: $font-family-poppins;
|
font-family: $font-family-poppins;
|
||||||
font-weight: $font-weight-semi-bold;
|
font-weight: $font-weight-semi-bold;
|
||||||
|
margin-top: 1rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
.theme-font-nunito {
|
.theme-font-nunito {
|
||||||
h1,h2,h3,h4,h5,h6 {
|
h1,h2,h3,h4,h5,h6 {
|
||||||
@@ -30,6 +32,7 @@ h1,h2,h3,h4,h5,h6 {
|
|||||||
p {
|
p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
line-height: 1.74; /* 26px */
|
line-height: 1.74; /* 26px */
|
||||||
|
color: #000;
|
||||||
@include breakpoint-less(md) {
|
@include breakpoint-less(md) {
|
||||||
line-height: 1.6; /* 24px */
|
line-height: 1.6; /* 24px */
|
||||||
}
|
}
|
||||||
@@ -106,3 +109,4 @@ i {
|
|||||||
.icon-sm {
|
.icon-sm {
|
||||||
i { font-size: 0.9em; }
|
i { font-size: 0.9em; }
|
||||||
}
|
}
|
||||||
|
|
||||||
51
assets/css/scss/components/_author-card.scss
Normal file
51
assets/css/scss/components/_author-card.scss
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
.author-card-item {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
padding: 1rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.author-card-image {
|
||||||
|
flex: 0 0 33.333333%;
|
||||||
|
max-width: 33.333333%;
|
||||||
|
padding-right: 1rem;
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.author-card-info {
|
||||||
|
flex: 0 0 66.666667%;
|
||||||
|
max-width: 66.666667%;
|
||||||
|
padding-left: 1rem;
|
||||||
|
|
||||||
|
.author-card-name {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.author-card-title {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.author-card-buttons {
|
||||||
|
.author-button-link {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
color: #007bff;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
155
assets/css/scss/components/_breadcrumb.scss
Normal file
155
assets/css/scss/components/_breadcrumb.scss
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
// Breadcrumb Component
|
||||||
|
.breadcrumb-wrapper {
|
||||||
|
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
||||||
|
border-bottom: 1px solid #dee2e6;
|
||||||
|
padding: 1rem 0;
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
padding: 0.75rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.4;
|
||||||
|
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
|
||||||
|
// &::after {
|
||||||
|
// content: "/";
|
||||||
|
// display: inline-block;
|
||||||
|
// color: #6c757d;
|
||||||
|
// margin-left: 0.75rem;
|
||||||
|
// opacity: 0.7;
|
||||||
|
// font-weight: 500;
|
||||||
|
|
||||||
|
// @media (max-width: 576px) {
|
||||||
|
// margin-left: 0.5rem;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #495057;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #007bff;
|
||||||
|
background-color: rgba(0, 123, 255, 0.1);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: 2px solid #007bff;
|
||||||
|
outline-offset: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
i.fas {
|
||||||
|
margin-right: 0.375rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
margin-right: 0.25rem;
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: #6c757d;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
max-width: 200px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
max-width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
max-width: 120px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Responsive truncation for long breadcrumbs
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.breadcrumb-item {
|
||||||
|
&:not(:last-child):not(:nth-last-child(2)) {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
& + .breadcrumb-item::before {
|
||||||
|
content: "...";
|
||||||
|
margin: 0 0.5rem;
|
||||||
|
color: #6c757d;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dark mode support
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
background: linear-gradient(135deg, #343a40 0%, #495057 100%);
|
||||||
|
border-bottom-color: #6c757d;
|
||||||
|
|
||||||
|
.breadcrumb {
|
||||||
|
.breadcrumb-item {
|
||||||
|
&:not(:last-child):not(:only-child)::after {
|
||||||
|
color: #adb5bd;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #dee2e6;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #66b2ff;
|
||||||
|
background-color: rgba(102, 178, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline-color: #66b2ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: #adb5bd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accessibility improvements
|
||||||
|
.sr-only {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
padding: 0;
|
||||||
|
margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
white-space: nowrap;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
144
assets/css/scss/components/_ez-toc.scss
Normal file
144
assets/css/scss/components/_ez-toc.scss
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
/* Styles for ez-toc-container */
|
||||||
|
#ez-toc-container {
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
padding: 16px 0;
|
||||||
|
box-shadow: none;
|
||||||
|
max-width: 100%;
|
||||||
|
margin: 16px 0;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.ez-toc-title {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-bottom: 2px solid #ddd;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ez-toc-cssicon-toggle-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
.ez-toc-icon-toggle-span {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ez-toc-list {
|
||||||
|
list-style-type: none;
|
||||||
|
padding-left: 0;
|
||||||
|
|
||||||
|
/* Base styles for all list items */
|
||||||
|
.ez-toc-link {
|
||||||
|
display: block;
|
||||||
|
padding: 8px 0;
|
||||||
|
color: #333;
|
||||||
|
text-decoration: none;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hierarchy-based alignment */
|
||||||
|
.ez-toc-list-level-1 {
|
||||||
|
padding-left: 0;
|
||||||
|
|
||||||
|
.ez-toc-link {
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ez-toc-list-level-2 {
|
||||||
|
padding-left: 20px;
|
||||||
|
|
||||||
|
.ez-toc-link {
|
||||||
|
font-size: 0.95em;
|
||||||
|
font-weight: 500;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ez-toc-list-level-3 {
|
||||||
|
padding-left: 40px;
|
||||||
|
|
||||||
|
.ez-toc-link {
|
||||||
|
font-size: 0.9em;
|
||||||
|
font-weight: 400;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ez-toc-list-level-4 {
|
||||||
|
padding-left: 60px;
|
||||||
|
|
||||||
|
.ez-toc-link {
|
||||||
|
font-size: 0.85em;
|
||||||
|
font-weight: 400;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add more levels if needed */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive styles */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
padding: 12px 0;
|
||||||
|
|
||||||
|
.ez-toc-title {
|
||||||
|
font-size: 1.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ez-toc-link {
|
||||||
|
padding: 6px 0;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adjust hierarchy padding for tablets */
|
||||||
|
.ez-toc-list-level-2 {
|
||||||
|
padding-left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ez-toc-list-level-3 {
|
||||||
|
padding-left: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ez-toc-list-level-4 {
|
||||||
|
padding-left: 45px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
padding: 10px 0;
|
||||||
|
|
||||||
|
.ez-toc-title {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ez-toc-link {
|
||||||
|
padding: 4px 0;
|
||||||
|
font-size: 0.85em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adjust hierarchy padding for mobile */
|
||||||
|
.ez-toc-list-level-2 {
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ez-toc-list-level-3 {
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ez-toc-list-level-4 {
|
||||||
|
padding-left: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
assets/css/scss/components/_table.scss
Normal file
27
assets/css/scss/components/_table.scss
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/* Styles for tables */
|
||||||
|
figure.wp-block-table table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 1.25rem;
|
||||||
|
margin-bottom: 1.25rem;
|
||||||
|
|
||||||
|
thead {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
border: 1px solid #dee2e6;
|
||||||
|
padding: 0.5rem;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background-color: #f1f3f5;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr:nth-child(odd) {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
//
|
//
|
||||||
// Button styles //
|
// Button styles //
|
||||||
//
|
//
|
||||||
a {
|
// a {
|
||||||
color: get-color("dark", 0.9);
|
// color: get-color("dark", 0.9);
|
||||||
text-decoration: none;
|
// text-decoration: none;
|
||||||
@include transition(linear 0.1s);
|
// @include transition(linear 0.1s);
|
||||||
&:hover {
|
// &:hover {
|
||||||
color: get-color("dark");
|
// color: get-color("dark");
|
||||||
text-decoration: none;
|
// text-decoration: none;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
button {
|
button {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
5
assets/css/scss/elements/_post-content.scss
Normal file
5
assets/css/scss/elements/_post-content.scss
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
.post-content {
|
||||||
|
p {
|
||||||
|
line-height: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,31 @@
|
|||||||
//
|
//
|
||||||
// Text Link Styles //
|
// Text Link Styles //
|
||||||
//
|
//
|
||||||
|
|
||||||
|
a {
|
||||||
|
// color: #007bff;
|
||||||
|
// color: get-color("dark", 0.8);
|
||||||
|
color: #000;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #0056b3;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-content {
|
||||||
|
a {
|
||||||
|
// color: #007bff;
|
||||||
|
color: get-color("dark", 0.8);
|
||||||
|
text-decoration: underline;
|
||||||
|
&:hover {
|
||||||
|
color: #0056b3;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
*[class*='text-link-'] {
|
*[class*='text-link-'] {
|
||||||
color: get-color("dark", 0.9);
|
color: get-color("dark", 0.9);
|
||||||
&:hover, &:focus {
|
&:hover, &:focus {
|
||||||
@@ -40,6 +40,7 @@
|
|||||||
@import "elements/testimonial";
|
@import "elements/testimonial";
|
||||||
@import "elements/text-link";
|
@import "elements/text-link";
|
||||||
@import "elements/timeline";
|
@import "elements/timeline";
|
||||||
|
@import "elements/post-content";
|
||||||
|
|
||||||
//
|
//
|
||||||
// Import Components //
|
// Import Components //
|
||||||
@@ -49,10 +50,15 @@
|
|||||||
@import "components/fullscreen-menu";
|
@import "components/fullscreen-menu";
|
||||||
@import "components/gallery";
|
@import "components/gallery";
|
||||||
@import "components/header";
|
@import "components/header";
|
||||||
|
@import "components/breadcrumb";
|
||||||
@import "components/masonry";
|
@import "components/masonry";
|
||||||
@import "components/portfolio";
|
@import "components/portfolio";
|
||||||
|
@import "components/table";
|
||||||
@import "components/preloader";
|
@import "components/preloader";
|
||||||
@import "components/section";
|
@import "components/section";
|
||||||
|
@import "components/_ez-toc";
|
||||||
|
@import "components/author-card";
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Import Utilities //
|
// Import Utilities //
|
||||||
@@ -63,3 +69,34 @@
|
|||||||
// Import Custom Styles //
|
// Import Custom Styles //
|
||||||
//
|
//
|
||||||
@import "helpers/custom";
|
@import "helpers/custom";
|
||||||
|
|
||||||
|
ul.wp-block-list {
|
||||||
|
margin-top: 1rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure.wp-block-image {
|
||||||
|
margin-top: 2rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Figcaption Styles //
|
||||||
|
//
|
||||||
|
figcaption, .wp-element-caption {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #aeaeae;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
text-align: center;
|
||||||
|
// display: flex;
|
||||||
|
// align-items: center;
|
||||||
|
// justify-content: center;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
figure.wp-block-image {
|
||||||
|
img {
|
||||||
|
margin: auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
assets/images/logo.png
Normal file
BIN
assets/images/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 MiB |
BIN
assets/images/og-logo.png
Normal file
BIN
assets/images/og-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
BIN
assets/images/og-logo.xcf
Normal file
BIN
assets/images/og-logo.xcf
Normal file
Binary file not shown.
46
docker-compose.yml
Normal file
46
docker-compose.yml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
networks:
|
||||||
|
default:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
|
|
||||||
|
services:
|
||||||
|
nginx:
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- ./public:/usr/share/nginx/html:ro
|
||||||
|
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
||||||
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
# Explicitly tell Traefik to expose this container
|
||||||
|
- traefik.enable=true
|
||||||
|
- traefik.docker.network=web
|
||||||
|
# HTTPS
|
||||||
|
- traefik.http.services.mg-hugo-service-secure.loadbalancer.server.port=80
|
||||||
|
- traefik.http.routers.mg-hugo-secure.service=mg-hugo-service-secure
|
||||||
|
- traefik.http.routers.mg-hugo-secure.entrypoints=websecure
|
||||||
|
- traefik.http.routers.mg-hugo-secure.tls.certresolver=le
|
||||||
|
- traefik.http.routers.mg-hugo-secure.rule=Host(`mistergeek.fr`, `www.mistergeek.fr`, `mistergeek.net`, `www.mistergeek.net`, `agence-webside.fr`, `www.agence-webside.fr`)
|
||||||
|
- traefik.http.middlewares.mg-hugo-secure-cache.compress=true
|
||||||
|
# HTTP
|
||||||
|
- traefik.http.services.mg-hugo-service-insecure.loadbalancer.server.port=80
|
||||||
|
- traefik.http.routers.mg-hugo-insecure.service=mg-hugo-service-insecure
|
||||||
|
- traefik.http.routers.mg-hugo-insecure.entrypoints=web
|
||||||
|
- traefik.http.routers.mg-hugo-insecure.rule=Host(`mistergeek.fr`, `www.mistergeek.fr`, `mistergeek.net`, `www.mistergeek.net`, `agence-webside.fr`, `www.agence-webside.fr`)
|
||||||
|
- traefik.http.middlewares.mg-hugo-insecure-cache.compress=true
|
||||||
|
- traefik.http.routers.traefik.tls=true
|
||||||
|
- traefik.http.routers.traefik.tls.certresolver=le
|
||||||
|
# GZIP
|
||||||
|
- traefik.http.routers.traefik.middlewares=traefik-compress
|
||||||
|
- traefik.http.middlewares.traefik-compress.compress=true
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
|
||||||
|
builder:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
volumes:
|
||||||
|
- .:/app
|
||||||
|
networks:
|
||||||
|
- default
|
||||||
|
# command: ls -l
|
||||||
206
docs/SEO-CHECKLIST.md
Normal file
206
docs/SEO-CHECKLIST.md
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
# SEO Implementation Checklist - Mistergeek
|
||||||
|
|
||||||
|
This checklist tracks the implementation of advanced SEO features for the Mistergeek Hugo site.
|
||||||
|
|
||||||
|
## ✅ Completed Features
|
||||||
|
|
||||||
|
### 1. Translation & Documentation
|
||||||
|
- [x] Translated `seo-recommendations-fr.md` to `seo-recommendations-en.md`
|
||||||
|
- [x] Created comprehensive English SEO documentation
|
||||||
|
|
||||||
|
### 2. Performance Optimizations
|
||||||
|
- [x] Created `layouts/partials/seo/head-performance.html`
|
||||||
|
- [x] Added DNS prefetch and preconnect for external resources
|
||||||
|
- [x] Implemented resource hints for fonts and CDNs
|
||||||
|
- [x] Added performance meta tags
|
||||||
|
|
||||||
|
### 3. Advanced Schema.org Structured Data
|
||||||
|
- [x] Enhanced `layouts/partials/seo/structured-data.html`
|
||||||
|
- [x] Created `layouts/partials/seo/schema-article.html` - TechArticle schema
|
||||||
|
- [x] Created `layouts/partials/seo/schema-tutorial.html` - HowTo schema
|
||||||
|
- [x] Created `layouts/partials/seo/schema-local.html` - Local SEO schema
|
||||||
|
|
||||||
|
### 4. Multi-language Support
|
||||||
|
- [x] Created `layouts/partials/seo/hreflang.html`
|
||||||
|
- [x] Added French regional variations (fr-fr, fr-be, fr-ca, fr-ch, fr-lu)
|
||||||
|
- [x] Implemented x-default fallback
|
||||||
|
|
||||||
|
### 5. Dynamic Meta Tags
|
||||||
|
- [x] Created `layouts/partials/seo/meta-dynamic.html`
|
||||||
|
- [x] Section-specific meta descriptions (tutorials, comparisons, security, wordpress)
|
||||||
|
- [x] Dynamic keywords based on content type
|
||||||
|
- [x] Article-specific meta tags
|
||||||
|
|
||||||
|
### 6. Local SEO Configuration
|
||||||
|
- [x] Added French market local SEO
|
||||||
|
- [x] Google My Business schema
|
||||||
|
- [x] Educational organization markup
|
||||||
|
- [x] Contact point and social profiles
|
||||||
|
|
||||||
|
### 7. Enhanced Configuration
|
||||||
|
- [x] Updated `hugo.toml` with advanced SEO settings
|
||||||
|
- [x] Added image optimization settings
|
||||||
|
- [x] Configured analytics integration
|
||||||
|
- [x] Added social media profiles
|
||||||
|
|
||||||
|
## 📋 Pre-launch SEO Checklist
|
||||||
|
|
||||||
|
### Technical SEO
|
||||||
|
- [ ] Verify all meta tags are present
|
||||||
|
- [ ] Test Schema.org markup with Google's Rich Results Test
|
||||||
|
- [ ] Validate structured data
|
||||||
|
- [ ] Check page loading speed (Core Web Vitals)
|
||||||
|
- [ ] Test mobile responsiveness
|
||||||
|
- [ ] Verify HTTPS implementation
|
||||||
|
- [ ] Check robots.txt file
|
||||||
|
|
||||||
|
### Content SEO
|
||||||
|
- [ ] Optimize meta descriptions for key pages
|
||||||
|
- [ ] Ensure unique title tags for all pages
|
||||||
|
- [ ] Add alt text to all images
|
||||||
|
- [ ] Create XML sitemap
|
||||||
|
- [ ] Set up 404 error page
|
||||||
|
- [ ] Implement canonical URLs
|
||||||
|
|
||||||
|
### Analytics & Monitoring
|
||||||
|
- [ ] Set up Google Analytics 4
|
||||||
|
- [ ] Configure Google Search Console
|
||||||
|
- [ ] Set up Bing Webmaster Tools
|
||||||
|
- [ ] Create Yandex Webmaster account
|
||||||
|
- [ ] Install SEO monitoring tools
|
||||||
|
|
||||||
|
### Social Media
|
||||||
|
- [ ] Verify Open Graph tags
|
||||||
|
- [ ] Test Twitter Cards
|
||||||
|
- [ ] Check social media meta tags
|
||||||
|
- [ ] Validate social media images
|
||||||
|
|
||||||
|
## 📊 Post-launch Monitoring
|
||||||
|
|
||||||
|
### Weekly Tasks
|
||||||
|
- [ ] Check Google Search Console for crawl errors
|
||||||
|
- [ ] Monitor 404 errors
|
||||||
|
- [ ] Review search performance metrics
|
||||||
|
- [ ] Check page loading speeds
|
||||||
|
|
||||||
|
### Monthly Tasks
|
||||||
|
- [ ] Analyze keyword positions
|
||||||
|
- [ ] Review backlink profile
|
||||||
|
- [ ] Audit internal linking
|
||||||
|
- [ ] Update outdated content
|
||||||
|
- [ ] Check competitor analysis
|
||||||
|
|
||||||
|
### Quarterly Tasks
|
||||||
|
- [ ] Comprehensive SEO audit
|
||||||
|
- [ ] Update Schema markup
|
||||||
|
- [ ] Review and update meta descriptions
|
||||||
|
- [ ] Analyze user engagement metrics
|
||||||
|
- [ ] Update local SEO information
|
||||||
|
|
||||||
|
## 🔧 Configuration Required
|
||||||
|
|
||||||
|
### Google Analytics Setup
|
||||||
|
1. Replace `G-XXXXXXXXXX` in `hugo.toml` with your actual Google Analytics 4 ID
|
||||||
|
2. Replace `GTM-XXXXXXX` with your Google Tag Manager ID
|
||||||
|
|
||||||
|
### Search Console Verification
|
||||||
|
1. Add your verification codes to `hugo.toml`:
|
||||||
|
```toml
|
||||||
|
[params.seo.verification]
|
||||||
|
google = "your-google-verification-code"
|
||||||
|
bing = "your-bing-verification-code"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Social Media URLs
|
||||||
|
Update the social media URLs in the schema files:
|
||||||
|
- `layouts/partials/seo/schema-local.html`
|
||||||
|
- `layouts/partials/seo/schema-article.html`
|
||||||
|
|
||||||
|
### Local SEO Information
|
||||||
|
Update the local business information in:
|
||||||
|
- `layouts/partials/seo/schema-local.html`
|
||||||
|
- Business address, phone, email
|
||||||
|
|
||||||
|
## 🎯 SEO Monitoring Tools
|
||||||
|
|
||||||
|
### Essential Tools
|
||||||
|
- Google Search Console
|
||||||
|
- Google Analytics 4
|
||||||
|
- Google PageSpeed Insights
|
||||||
|
- Schema Markup Validator
|
||||||
|
- Mobile-Friendly Test
|
||||||
|
|
||||||
|
### Advanced Tools
|
||||||
|
- Ahrefs or SEMrush
|
||||||
|
- Screaming Frog SEO Spider
|
||||||
|
- GTmetrix
|
||||||
|
- Bing Webmaster Tools
|
||||||
|
- Yandex Webmaster
|
||||||
|
|
||||||
|
### French Market Tools
|
||||||
|
- Yooda Insight (French SEO tool)
|
||||||
|
- SEMrush.fr
|
||||||
|
- Ahrefs France
|
||||||
|
- Local SEO France directories
|
||||||
|
|
||||||
|
## 🚀 Next Steps
|
||||||
|
|
||||||
|
1. **Immediate (Week 1)**
|
||||||
|
- Set up Google Analytics 4
|
||||||
|
- Configure Google Search Console
|
||||||
|
- Submit sitemap to search engines
|
||||||
|
- Test all Schema markup
|
||||||
|
|
||||||
|
2. **Short-term (Month 1)**
|
||||||
|
- Monitor search performance
|
||||||
|
- Optimize top pages
|
||||||
|
- Build initial backlinks
|
||||||
|
- Create social media profiles
|
||||||
|
|
||||||
|
3. **Long-term (Quarter 1)**
|
||||||
|
- Content optimization
|
||||||
|
- Link building campaigns
|
||||||
|
- Local SEO optimization
|
||||||
|
- Competitor analysis
|
||||||
|
|
||||||
|
## 📈 Key Performance Indicators (KPIs)
|
||||||
|
|
||||||
|
### Traffic Metrics
|
||||||
|
- Organic search traffic growth
|
||||||
|
- Click-through rate (CTR)
|
||||||
|
- Bounce rate
|
||||||
|
- Average session duration
|
||||||
|
- Pages per session
|
||||||
|
|
||||||
|
### Technical Metrics
|
||||||
|
- Page loading speed
|
||||||
|
- Core Web Vitals scores
|
||||||
|
- Mobile usability score
|
||||||
|
- Schema markup errors
|
||||||
|
- Crawl errors
|
||||||
|
|
||||||
|
### Business Metrics
|
||||||
|
- Keyword rankings
|
||||||
|
- Conversion rate
|
||||||
|
- Return on investment (ROI)
|
||||||
|
- Local search visibility
|
||||||
|
- Brand mentions
|
||||||
|
|
||||||
|
## 🛠️ Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
- Schema markup errors: Use Google's Rich Results Test
|
||||||
|
- Slow loading: Check PageSpeed Insights
|
||||||
|
- Mobile issues: Use Mobile-Friendly Test
|
||||||
|
- Indexing problems: Check Search Console coverage report
|
||||||
|
|
||||||
|
### Support Resources
|
||||||
|
- Google SEO Starter Guide (French)
|
||||||
|
- Schema.org documentation
|
||||||
|
- Hugo community forums
|
||||||
|
- French SEO communities
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Last Updated**: 2024-01-15
|
||||||
|
**Next Review**: 2024-02-15
|
||||||
114
docs/SEO-IMPROVEMENTS.md
Normal file
114
docs/SEO-IMPROVEMENTS.md
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
# SEO Improvements Documentation
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
This project has been enhanced with comprehensive SEO capabilities using a modular partial system in Hugo.
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
```
|
||||||
|
layouts/partials/seo/
|
||||||
|
├── seo-config.html # Main SEO configuration loader
|
||||||
|
├── seo-meta.html # Core SEO meta tags
|
||||||
|
├── opengraph.html # Open Graph tags for social media
|
||||||
|
├── twitter-cards.html # Twitter Card tags
|
||||||
|
├── structured-data.html # JSON-LD schema markup
|
||||||
|
└── favicons.html # Favicon variations and PWA support
|
||||||
|
```
|
||||||
|
|
||||||
|
## Features Added
|
||||||
|
|
||||||
|
### 1. Core SEO Meta Tags
|
||||||
|
- Dynamic meta description
|
||||||
|
- Keywords (with fallback)
|
||||||
|
- Author information
|
||||||
|
- Canonical URLs
|
||||||
|
- Robots meta tags
|
||||||
|
- Dublin Core metadata
|
||||||
|
- Geo tags (if configured)
|
||||||
|
|
||||||
|
### 2. Open Graph Tags
|
||||||
|
- og:title, og:description, og:image
|
||||||
|
- og:type (article/website)
|
||||||
|
- og:site_name
|
||||||
|
- og:url
|
||||||
|
- Article-specific tags for blog posts
|
||||||
|
|
||||||
|
### 3. Twitter Cards
|
||||||
|
- twitter:card (summary_large_image)
|
||||||
|
- twitter:title, twitter:description
|
||||||
|
- twitter:image
|
||||||
|
- Site and creator handles
|
||||||
|
|
||||||
|
### 4. Structured Data (JSON-LD)
|
||||||
|
- WebSite schema
|
||||||
|
- Article schema for blog posts
|
||||||
|
- BreadcrumbList schema
|
||||||
|
- Organization schema
|
||||||
|
|
||||||
|
### 5. Favicon & PWA Support
|
||||||
|
- Multiple favicon sizes
|
||||||
|
- Apple Touch Icons
|
||||||
|
- Android icons
|
||||||
|
- PWA manifest.json
|
||||||
|
- Theme colors
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Hugo Configuration (hugo.toml)
|
||||||
|
```toml
|
||||||
|
[params.seo]
|
||||||
|
description = "Your site description"
|
||||||
|
keywords = ["keyword1", "keyword2"]
|
||||||
|
author = "Your Name"
|
||||||
|
theme_color = "#007bff"
|
||||||
|
default_image = "/images/og-default.jpg"
|
||||||
|
logo = "/images/logo.png"
|
||||||
|
|
||||||
|
[params.seo.twitter]
|
||||||
|
site = "@yourhandle"
|
||||||
|
creator = "@yourhandle"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Content Frontmatter
|
||||||
|
Add to your content's frontmatter:
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
title: "Your Post Title"
|
||||||
|
description: "Detailed description for SEO"
|
||||||
|
keywords: ["seo", "hugo", "optimization"]
|
||||||
|
author: "Author Name"
|
||||||
|
image: "/images/post-image.jpg"
|
||||||
|
robots: "index, follow"
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing & Validation
|
||||||
|
|
||||||
|
### Recommended Tools
|
||||||
|
- Google Rich Results Test: https://search.google.com/test/rich-results
|
||||||
|
- Facebook Sharing Debugger: https://developers.facebook.com/tools/debug/
|
||||||
|
- Twitter Card Validator: https://cards-dev.twitter.com/validator
|
||||||
|
- Schema.org Validator: https://validator.schema.org/
|
||||||
|
|
||||||
|
### Validation Checklist
|
||||||
|
- [ ] Meta tags present in page source
|
||||||
|
- [ ] Open Graph tags validate
|
||||||
|
- [ ] Twitter Cards validate
|
||||||
|
- [ ] JSON-LD schema validates
|
||||||
|
- [ ] Favicons load correctly
|
||||||
|
- [ ] Canonical URLs are correct
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
1. Generate favicon files for all sizes (use a favicon generator)
|
||||||
|
2. Create og-default.jpg and twitter-default.jpg images
|
||||||
|
3. Set up Google Search Console and add verification code
|
||||||
|
4. Set up Bing Webmaster Tools
|
||||||
|
5. Test with social media sharing
|
||||||
|
|
||||||
|
## Fallback Values
|
||||||
|
The system includes intelligent fallback values:
|
||||||
|
- Description: Page → Site → Title
|
||||||
|
- Keywords: Page → Site → Empty string
|
||||||
|
- Image: Page → Site → Default
|
||||||
|
- Author: Page → Site → Site Title
|
||||||
|
|
||||||
|
All SEO improvements have been successfully implemented!
|
||||||
363
docs/seo-recommendations-en.md
Normal file
363
docs/seo-recommendations-en.md
Normal file
@@ -0,0 +1,363 @@
|
|||||||
|
# Advanced SEO Recommendations - Hugo Mistergeek
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This document provides advanced SEO recommendations specifically tailored for the Hugo Mistergeek blog, a French-speaking site covering technology and computing topics. The recommendations take into account the existing SEO implementation and propose targeted improvements to maximize visibility in the French-speaking market.
|
||||||
|
|
||||||
|
## Current SEO Implementation Status ✅
|
||||||
|
|
||||||
|
### ✅ Successfully Implemented
|
||||||
|
- **Essential meta tags** (description, keywords, author)
|
||||||
|
- **Open Graph** for social networks
|
||||||
|
- **Twitter Cards** with optimized images
|
||||||
|
- **Schema.org** (JSON-LD) for structured data
|
||||||
|
- **Favicons** multi-format and PWA support
|
||||||
|
- **Canonical URLs** and hreflang
|
||||||
|
- **XML sitemap generation** via Hugo
|
||||||
|
|
||||||
|
## SEO Improvement Recommendations
|
||||||
|
|
||||||
|
### 1. French Content Optimization
|
||||||
|
|
||||||
|
#### URL Structure
|
||||||
|
```yaml
|
||||||
|
# Recommendation: Optimize for French
|
||||||
|
Old structure: /post/2023-11-wordpress-creation-site/
|
||||||
|
New structure: /tutorials/wordpress/create-wordpress-site-complete-guide/
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Keyword Strategy for French Market
|
||||||
|
```yaml
|
||||||
|
# Main keywords (high competition)
|
||||||
|
- "computer tutorial"
|
||||||
|
- "technology guide"
|
||||||
|
- "IT solutions"
|
||||||
|
|
||||||
|
# Long-tail keywords (French)
|
||||||
|
- "how to create a WordPress site in French"
|
||||||
|
- "best free antivirus for Windows 10 in 2024"
|
||||||
|
- "computer security tutorial for beginners"
|
||||||
|
|
||||||
|
# Language variations
|
||||||
|
- English: "computer tutorial" → French: "tutoriel informatique"
|
||||||
|
- English: "how to" → French: "how", "guide", "tutorial"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Advanced Technical Optimization
|
||||||
|
|
||||||
|
#### Performance and Core Web Vitals
|
||||||
|
```html
|
||||||
|
<!-- Add in layouts/partials/head-performance.html -->
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link rel="dns-prefetch" href="//www.google-analytics.com">
|
||||||
|
<link rel="dns-prefetch" href="//googletagmanager.com">
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Strategic Lazy Loading
|
||||||
|
```html
|
||||||
|
<!-- For images in articles -->
|
||||||
|
<img
|
||||||
|
src="/images/placeholder.svg"
|
||||||
|
data-src="/images/article-image.jpg"
|
||||||
|
alt="WordPress Tutorial - Create a Professional Site"
|
||||||
|
loading="lazy"
|
||||||
|
width="800"
|
||||||
|
height="400"
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Enhanced Schema.org
|
||||||
|
|
||||||
|
#### Article Schema with Detailed Author
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "TechArticle",
|
||||||
|
"headline": "Complete WordPress Guide 2024",
|
||||||
|
"description": "Create your WordPress site in French with this step-by-step guide",
|
||||||
|
"author": {
|
||||||
|
"@type": "Person",
|
||||||
|
"name": "Mistergeek",
|
||||||
|
"url": "https://www.mistergeek.net/",
|
||||||
|
"sameAs": [
|
||||||
|
"https://twitter.com/mistergeekfrance",
|
||||||
|
"https://www.linkedin.com/in/mistergeek"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"publisher": {
|
||||||
|
"@type": "Organization",
|
||||||
|
"name": "Mistergeek",
|
||||||
|
"logo": {
|
||||||
|
"@type": "ImageObject",
|
||||||
|
"url": "https://www.mistergeek.net/assets/images/logo.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inLanguage": "fr-FR",
|
||||||
|
"datePublished": "2024-01-15",
|
||||||
|
"dateModified": "2024-01-15",
|
||||||
|
"keywords": ["wordpress", "french tutorial", "create website"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### BreadcrumbList for Navigation
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "BreadcrumbList",
|
||||||
|
"itemListElement": [
|
||||||
|
{
|
||||||
|
"@type": "ListItem",
|
||||||
|
"position": 1,
|
||||||
|
"name": "Home",
|
||||||
|
"item": "https://www.mistergeek.net/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "ListItem",
|
||||||
|
"position": 2,
|
||||||
|
"name": "Tutorials",
|
||||||
|
"item": "https://www.mistergeek.net/tutorials/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "ListItem",
|
||||||
|
"position": 3,
|
||||||
|
"name": "WordPress",
|
||||||
|
"item": "https://www.mistergeek.net/tutorials/wordpress/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Local SEO for French Market
|
||||||
|
|
||||||
|
#### Google My Business
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "Organization",
|
||||||
|
"name": "Mistergeek",
|
||||||
|
"url": "https://www.mistergeek.net",
|
||||||
|
"logo": "https://www.mistergeek.net/assets/images/logo.png",
|
||||||
|
"contactPoint": {
|
||||||
|
"@type": "ContactPoint",
|
||||||
|
"contactType": "support",
|
||||||
|
"email": "contact@mistergeek.net",
|
||||||
|
"availableLanguage": ["French"]
|
||||||
|
},
|
||||||
|
"sameAs": [
|
||||||
|
"https://twitter.com/mistergeekfrance",
|
||||||
|
"https://www.youtube.com/@mistergeek"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Meta Tags Enhancement
|
||||||
|
|
||||||
|
#### Dynamic Meta Tags by Content Type
|
||||||
|
```go
|
||||||
|
<!-- layouts/partials/seo/meta-dynamic.html -->
|
||||||
|
{{- if eq .Section "tutorials" }}
|
||||||
|
<meta name="description" content="Complete tutorial {{ .Title }}. Learn {{ .Params.skill }} in French with our step-by-step guide.">
|
||||||
|
<meta name="keywords" content="tutorial {{ .Params.category }}, french guide {{ .Params.skill }}, {{ .Title | lower }}">
|
||||||
|
{{- else if eq .Section "comparisons" }}
|
||||||
|
<meta name="description" content="Detailed comparison {{ .Title }}. Which to choose in 2024? Complete reviews and tests.">
|
||||||
|
{{- end }}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Multi-language Optimization
|
||||||
|
|
||||||
|
#### hreflang for French
|
||||||
|
```html
|
||||||
|
<!-- In layouts/partials/seo/hreflang.html -->
|
||||||
|
<link rel="alternate" hreflang="fr-fr" href="https://www.mistergeek.net{{ .RelPermalink }}" />
|
||||||
|
<link rel="alternate" hreflang="fr-be" href="https://www.mistergeek.net/be{{ .RelPermalink }}" />
|
||||||
|
<link rel="alternate" hreflang="fr-ca" href="https://www.mistergeek.net/ca{{ .RelPermalink }}" />
|
||||||
|
<link rel="alternate" hreflang="x-default" href="https://www.mistergeek.net{{ .RelPermalink }}" />
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7. Tutorial Data Structure
|
||||||
|
|
||||||
|
#### Tutorial Schema
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "HowTo",
|
||||||
|
"name": "Create a WordPress Site",
|
||||||
|
"description": "Complete guide to create your WordPress site in French",
|
||||||
|
"image": "https://www.mistergeek.net/assets/images/wordpress-guide.jpg",
|
||||||
|
"totalTime": "PT30M",
|
||||||
|
"estimatedCost": {
|
||||||
|
"@type": "MonetaryAmount",
|
||||||
|
"currency": "EUR",
|
||||||
|
"value": "0"
|
||||||
|
},
|
||||||
|
"supply": [
|
||||||
|
{
|
||||||
|
"@type": "HowToSupply",
|
||||||
|
"name": "WordPress"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "HowToSupply",
|
||||||
|
"name": "Web hosting"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tool": [
|
||||||
|
{
|
||||||
|
"@type": "HowToTool",
|
||||||
|
"name": "WordPress"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"step": [
|
||||||
|
{
|
||||||
|
"@type": "HowToStep",
|
||||||
|
"name": "WordPress Installation",
|
||||||
|
"text": "Download and install WordPress"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8. Image Optimization
|
||||||
|
|
||||||
|
#### SEO Image Structure
|
||||||
|
```yaml
|
||||||
|
# config.toml - Image configuration
|
||||||
|
[imaging]
|
||||||
|
quality = 75
|
||||||
|
resampleFilter = "lanczos"
|
||||||
|
|
||||||
|
[params.images]
|
||||||
|
# Recommended dimensions for SEO
|
||||||
|
og_image = "1200x630"
|
||||||
|
twitter_image = "1200x675"
|
||||||
|
thumbnail = "400x300"
|
||||||
|
hero = "1920x1080"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9. Strategic Internal Linking
|
||||||
|
|
||||||
|
#### Link Structure
|
||||||
|
```markdown
|
||||||
|
<!-- In content -->
|
||||||
|
To go further, discover:
|
||||||
|
- [Complete WordPress Guide](/tutorials/wordpress/complete-guide/)
|
||||||
|
- [Best WordPress Plugins](/comparisons/plugins-wordpress-2024/)
|
||||||
|
- [WordPress Security](/security/wordpress-security-maximum/)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10. Advanced Hugo.toml Configuration
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Advanced SEO Configuration
|
||||||
|
[params.seo]
|
||||||
|
# Existing configuration...
|
||||||
|
|
||||||
|
# Analytics
|
||||||
|
google_analytics = "G-XXXXXXXXXX"
|
||||||
|
google_tag_manager = "GTM-XXXXXXX"
|
||||||
|
|
||||||
|
# Rich Snippets
|
||||||
|
enable_search_box = true
|
||||||
|
enable_sitelinks_searchbox = true
|
||||||
|
|
||||||
|
# Social
|
||||||
|
facebook_page = "mistergeek.fr"
|
||||||
|
twitter_handle = "@mistergeekfrance"
|
||||||
|
youtube_channel = "UCXXXXXXXXXXXXXXXXXXX"
|
||||||
|
|
||||||
|
# Local SEO
|
||||||
|
[params.seo.local]
|
||||||
|
enabled = true
|
||||||
|
country = "FR"
|
||||||
|
language = "fr"
|
||||||
|
region = "Île-de-France"
|
||||||
|
|
||||||
|
[markup.goldmark.renderer]
|
||||||
|
unsafe = true
|
||||||
|
|
||||||
|
[markup.highlight]
|
||||||
|
style = "github"
|
||||||
|
lineNos = true
|
||||||
|
codeFences = true
|
||||||
|
|
||||||
|
[sitemap]
|
||||||
|
changefreq = "weekly"
|
||||||
|
filename = "sitemap.xml"
|
||||||
|
priority = 0.5
|
||||||
|
|
||||||
|
[privacy]
|
||||||
|
[privacy.googleAnalytics]
|
||||||
|
disable = false
|
||||||
|
respectDoNotTrack = true
|
||||||
|
```
|
||||||
|
|
||||||
|
### 11. Monitoring and Tools
|
||||||
|
|
||||||
|
#### SEO Monitoring Tools
|
||||||
|
```yaml
|
||||||
|
# To configure
|
||||||
|
- Google Search Console: verify site
|
||||||
|
- Google Analytics 4: advanced tracking
|
||||||
|
- Bing Webmaster Tools: Bing indexing
|
||||||
|
- Yandex Webmaster: Russian market
|
||||||
|
- Ahrefs/SEMrush: competitive analysis
|
||||||
|
```
|
||||||
|
|
||||||
|
### 12. Launch Checklist
|
||||||
|
|
||||||
|
#### Pre-launch SEO
|
||||||
|
- [ ] Check all meta tags
|
||||||
|
- [ ] Test rich snippets
|
||||||
|
- [ ] Validate sitemap.xml
|
||||||
|
- [ ] Configure Google Search Console
|
||||||
|
- [ ] Configure Google Analytics 4
|
||||||
|
- [ ] Test loading speed
|
||||||
|
- [ ] Check for missing images
|
||||||
|
- [ ] Test broken links
|
||||||
|
- [ ] Validate Schema.org markup
|
||||||
|
|
||||||
|
#### Post-launch
|
||||||
|
- [ ] Submit sitemap to Google
|
||||||
|
- [ ] Monitor 404 errors
|
||||||
|
- [ ] Analyze keyword positioning
|
||||||
|
- [ ] Optimize pages with low CTR
|
||||||
|
- [ ] Improve pages with high bounce rate
|
||||||
|
|
||||||
|
### 13. Performance Measurement
|
||||||
|
|
||||||
|
#### SEO KPIs to track
|
||||||
|
```yaml
|
||||||
|
Monthly:
|
||||||
|
- Average keyword position
|
||||||
|
- Click-through rate (CTR)
|
||||||
|
- Indexed pages
|
||||||
|
- Crawl errors
|
||||||
|
- Loading speed
|
||||||
|
|
||||||
|
Quarterly:
|
||||||
|
- Keyword market share
|
||||||
|
- Quality backlinks
|
||||||
|
- Authority Score (Domain Rating)
|
||||||
|
- Organic traffic vs goals
|
||||||
|
```
|
||||||
|
|
||||||
|
## Additional Resources
|
||||||
|
|
||||||
|
### French SEO Tools
|
||||||
|
- **Google Search Console** - Main monitoring
|
||||||
|
- **Screaming Frog** - Technical audit
|
||||||
|
- **Ahrefs/SEMrush** - Competitive analysis
|
||||||
|
- **GTmetrix** - Performance
|
||||||
|
- **Schema Markup Validator** - Rich snippets
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
- [Google SEO Starter Guide - French](https://support.google.com/webmasters/answer/7451184?hl=fr)
|
||||||
|
- [Bing Webmaster Guidelines](https://www.bing.com/webmaster/help/webmaster-guidelines-30fba23a)
|
||||||
|
- [Schema.org Documentation](https://schema.org/docs/documents.html)
|
||||||
|
|
||||||
|
### Community Support
|
||||||
|
- [Webmaster Help Community - French](https://support.google.com/webmasters/community?hl=fr)
|
||||||
|
- [Reddit r/SEO](https://www.reddit.com/r/SEO/)
|
||||||
|
- [Search Engine Journal - French](https://www.searchenginejournal.com/tag/french/)
|
||||||
|
|
||||||
|
This SEO guide is specifically adapted for the French-speaking market and takes into account the linguistic and cultural particularities of the Mistergeek target audience.
|
||||||
363
docs/seo-recommendations-fr.md
Normal file
363
docs/seo-recommendations-fr.md
Normal file
@@ -0,0 +1,363 @@
|
|||||||
|
# Recommandations SEO Avancées - Hugo Mistergeek
|
||||||
|
|
||||||
|
## Vue d'ensemble
|
||||||
|
|
||||||
|
Ce document fournit des recommandations SEO avancées spécifiquement adaptées au blog Hugo Mistergeek, un site francophone traitant de technologie et d'informatique. Les recommandations prennent en compte l'implémentation SEO existante et proposent des améliorations ciblées pour maximiser la visibilité sur le marché francophone.
|
||||||
|
|
||||||
|
## État Actuel de l'implémentation SEO ✅
|
||||||
|
|
||||||
|
### ✅ Implémenté avec succès
|
||||||
|
- **Meta tags essentiels** (description, keywords, author)
|
||||||
|
- **Open Graph** pour les réseaux sociaux
|
||||||
|
- **Twitter Cards** avec images optimisées
|
||||||
|
- **Schema.org** (JSON-LD) pour les données structurées
|
||||||
|
- **Favicons** multi-formats et support PWA
|
||||||
|
- **Canonical URLs** et hreflang
|
||||||
|
- **Génération de sitemap XML** via Hugo
|
||||||
|
|
||||||
|
## Recommandations d'Amélioration SEO
|
||||||
|
|
||||||
|
### 1. Optimisation du Contenu Francophone
|
||||||
|
|
||||||
|
#### Structure des URLs
|
||||||
|
```yaml
|
||||||
|
# Recommandation: Optimiser pour le français
|
||||||
|
Ancienne structure: /post/2023-11-wordpress-creation-site/
|
||||||
|
Nouvelle structure: /tutoriels/wordpress/creer-site-wordpress-guide-complet/
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Stratégie de Mots-clés pour le Marché Francophone
|
||||||
|
```yaml
|
||||||
|
# Mots-clés principaux (haute concurrence)
|
||||||
|
- "tutoriel informatique"
|
||||||
|
- "guide technologie"
|
||||||
|
- "solutions informatiques"
|
||||||
|
|
||||||
|
# Mots-clés longue traîne (français)
|
||||||
|
- "comment créer un site WordPress en français"
|
||||||
|
- "meilleur antivirus gratuit pour Windows 10 en 2024"
|
||||||
|
- "tutoriel sécurité informatique débutant"
|
||||||
|
|
||||||
|
# Variations linguistiques
|
||||||
|
- Anglais: "computer tutorial" → Français: "tutoriel informatique"
|
||||||
|
- Anglais: "how to" → Français: "comment", "guide", "tutoriel"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Optimisation Technique Avancée
|
||||||
|
|
||||||
|
#### Performance et Core Web Vitals
|
||||||
|
```html
|
||||||
|
<!-- Ajouter dans layouts/partials/head-performance.html -->
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link rel="dns-prefetch" href="//www.google-analytics.com">
|
||||||
|
<link rel="dns-prefetch" href="//googletagmanager.com">
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Lazy Loading Stratégique
|
||||||
|
```html
|
||||||
|
<!-- Pour les images dans les articles -->
|
||||||
|
<img
|
||||||
|
src="/images/placeholder.svg"
|
||||||
|
data-src="/images/article-image.jpg"
|
||||||
|
alt="Tutoriel WordPress - Créer un site professionnel"
|
||||||
|
loading="lazy"
|
||||||
|
width="800"
|
||||||
|
height="400"
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Schema.org Amélioré
|
||||||
|
|
||||||
|
#### Article Schema avec Auteur Détaillé
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "TechArticle",
|
||||||
|
"headline": "Guide Complet WordPress 2024",
|
||||||
|
"description": "Créez votre site WordPress en français avec ce guide étape par étape",
|
||||||
|
"author": {
|
||||||
|
"@type": "Person",
|
||||||
|
"name": "Mistergeek",
|
||||||
|
"url": "https://www.mistergeek.net/",
|
||||||
|
"sameAs": [
|
||||||
|
"https://twitter.com/mistergeekfrance",
|
||||||
|
"https://www.linkedin.com/in/mistergeek"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"publisher": {
|
||||||
|
"@type": "Organization",
|
||||||
|
"name": "Mistergeek",
|
||||||
|
"logo": {
|
||||||
|
"@type": "ImageObject",
|
||||||
|
"url": "https://www.mistergeek.net/assets/images/logo.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inLanguage": "fr-FR",
|
||||||
|
"datePublished": "2024-01-15",
|
||||||
|
"dateModified": "2024-01-15",
|
||||||
|
"keywords": ["wordpress", "tutoriel français", "créer site web"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### BreadcrumbList pour la Navigation
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "BreadcrumbList",
|
||||||
|
"itemListElement": [
|
||||||
|
{
|
||||||
|
"@type": "ListItem",
|
||||||
|
"position": 1,
|
||||||
|
"name": "Accueil",
|
||||||
|
"item": "https://www.mistergeek.net/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "ListItem",
|
||||||
|
"position": 2,
|
||||||
|
"name": "Tutoriels",
|
||||||
|
"item": "https://www.mistergeek.net/tutoriels/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "ListItem",
|
||||||
|
"position": 3,
|
||||||
|
"name": "WordPress",
|
||||||
|
"item": "https://www.mistergeek.net/tutoriels/wordpress/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. SEO Local pour le Marché Français
|
||||||
|
|
||||||
|
#### Google My Business
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "Organization",
|
||||||
|
"name": "Mistergeek",
|
||||||
|
"url": "https://www.mistergeek.net",
|
||||||
|
"logo": "https://www.mistergeek.net/assets/images/logo.png",
|
||||||
|
"contactPoint": {
|
||||||
|
"@type": "ContactPoint",
|
||||||
|
"contactType": "support",
|
||||||
|
"email": "contact@mistergeek.net",
|
||||||
|
"availableLanguage": ["French"]
|
||||||
|
},
|
||||||
|
"sameAs": [
|
||||||
|
"https://twitter.com/mistergeekfrance",
|
||||||
|
"https://www.youtube.com/@mistergeek"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Amélioration des Meta Tags
|
||||||
|
|
||||||
|
#### Meta Tags Dynamiques par Type de Contenu
|
||||||
|
```go
|
||||||
|
<!-- layouts/partials/seo/meta-dynamic.html -->
|
||||||
|
{{- if eq .Section "tutoriels" }}
|
||||||
|
<meta name="description" content="Tutoriel complet {{ .Title }}. Apprenez {{ .Params.skill }} en français avec notre guide étape par étape.">
|
||||||
|
<meta name="keywords" content="tutoriel {{ .Params.category }}, guide français {{ .Params.skill }}, {{ .Title | lower }}">
|
||||||
|
{{- else if eq .Section "comparatifs" }}
|
||||||
|
<meta name="description" content="Comparatif détaillé {{ .Title }}. Lequel choisir en 2024 ? Avis et tests complets.">
|
||||||
|
{{- end }}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Optimisation Multilingue
|
||||||
|
|
||||||
|
#### hreflang pour le Français
|
||||||
|
```html
|
||||||
|
<!-- Dans layouts/partials/seo/hreflang.html -->
|
||||||
|
<link rel="alternate" hreflang="fr-fr" href="https://www.mistergeek.net{{ .RelPermalink }}" />
|
||||||
|
<link rel="alternate" hreflang="fr-be" href="https://www.mistergeek.net/be{{ .RelPermalink }}" />
|
||||||
|
<link rel="alternate" hreflang="fr-ca" href="https://www.mistergeek.net/ca{{ .RelPermalink }}" />
|
||||||
|
<link rel="alternate" hreflang="x-default" href="https://www.mistergeek.net{{ .RelPermalink }}" />
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7. Structure des Données pour les Tutoriels
|
||||||
|
|
||||||
|
#### Tutoriel Schema
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "HowTo",
|
||||||
|
"name": "Créer un site WordPress",
|
||||||
|
"description": "Guide complet pour créer votre site WordPress en français",
|
||||||
|
"image": "https://www.mistergeek.net/assets/images/wordpress-guide.jpg",
|
||||||
|
"totalTime": "PT30M",
|
||||||
|
"estimatedCost": {
|
||||||
|
"@type": "MonetaryAmount",
|
||||||
|
"currency": "EUR",
|
||||||
|
"value": "0"
|
||||||
|
},
|
||||||
|
"supply": [
|
||||||
|
{
|
||||||
|
"@type": "HowToSupply",
|
||||||
|
"name": "WordPress"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "HowToSupply",
|
||||||
|
"name": "Hébergement web"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tool": [
|
||||||
|
{
|
||||||
|
"@type": "HowToTool",
|
||||||
|
"name": "WordPress"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"step": [
|
||||||
|
{
|
||||||
|
"@type": "HowToStep",
|
||||||
|
"name": "Installation WordPress",
|
||||||
|
"text": "Téléchargez et installez WordPress"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8. Optimisation des Images
|
||||||
|
|
||||||
|
#### Structure des Images SEO
|
||||||
|
```yaml
|
||||||
|
# config.toml - Configuration des images
|
||||||
|
[imaging]
|
||||||
|
quality = 75
|
||||||
|
resampleFilter = "lanczos"
|
||||||
|
|
||||||
|
[params.images]
|
||||||
|
# Dimensions recommandées pour le SEO
|
||||||
|
og_image = "1200x630"
|
||||||
|
twitter_image = "1200x675"
|
||||||
|
thumbnail = "400x300"
|
||||||
|
hero = "1920x1080"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9. Internal Linking Stratégique
|
||||||
|
|
||||||
|
#### Structure de Liens
|
||||||
|
```markdown
|
||||||
|
<!-- Dans le contenu -->
|
||||||
|
Pour aller plus loin, découvrez :
|
||||||
|
- [Guide complet WordPress](/tutoriels/wordpress/guide-complet/)
|
||||||
|
- [Meilleurs plugins WordPress](/comparatifs/plugins-wordpress-2024/)
|
||||||
|
- [Sécurité WordPress](/securite/wordpress-securite-maximale/)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10. Configuration Avancée Hugo.toml
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# SEO Configuration avancée
|
||||||
|
[params.seo]
|
||||||
|
# Configuration existante...
|
||||||
|
|
||||||
|
# Analytics
|
||||||
|
google_analytics = "G-XXXXXXXXXX"
|
||||||
|
google_tag_manager = "GTM-XXXXXXX"
|
||||||
|
|
||||||
|
# Rich Snippets
|
||||||
|
enable_search_box = true
|
||||||
|
enable_sitelinks_searchbox = true
|
||||||
|
|
||||||
|
# Social
|
||||||
|
facebook_page = "mistergeek.fr"
|
||||||
|
twitter_handle = "@mistergeekfrance"
|
||||||
|
youtube_channel = "UCXXXXXXXXXXXXXXXXXXX"
|
||||||
|
|
||||||
|
# Local SEO
|
||||||
|
[params.seo.local]
|
||||||
|
enabled = true
|
||||||
|
country = "FR"
|
||||||
|
language = "fr"
|
||||||
|
region = "Île-de-France"
|
||||||
|
|
||||||
|
[markup.goldmark.renderer]
|
||||||
|
unsafe = true
|
||||||
|
|
||||||
|
[markup.highlight]
|
||||||
|
style = "github"
|
||||||
|
lineNos = true
|
||||||
|
codeFences = true
|
||||||
|
|
||||||
|
[sitemap]
|
||||||
|
changefreq = "weekly"
|
||||||
|
filename = "sitemap.xml"
|
||||||
|
priority = 0.5
|
||||||
|
|
||||||
|
[privacy]
|
||||||
|
[privacy.googleAnalytics]
|
||||||
|
disable = false
|
||||||
|
respectDoNotTrack = true
|
||||||
|
```
|
||||||
|
|
||||||
|
### 11. Monitoring et Outils
|
||||||
|
|
||||||
|
#### Outils de Monitoring SEO
|
||||||
|
```yaml
|
||||||
|
# À configurer
|
||||||
|
- Google Search Console: vérifier le site
|
||||||
|
- Google Analytics 4: tracking avancé
|
||||||
|
- Bing Webmaster Tools: indexation Bing
|
||||||
|
- Yandex Webmaster: marché russe
|
||||||
|
- Ahrefs/SEMrush: analyse concurrentielle
|
||||||
|
```
|
||||||
|
|
||||||
|
### 12. Checklist de Lancement
|
||||||
|
|
||||||
|
#### Pré-lancement SEO
|
||||||
|
- [ ] Vérifier tous les meta tags
|
||||||
|
- [ ] Tester les rich snippets
|
||||||
|
- [ ] Valider le sitemap.xml
|
||||||
|
- [ ] Configurer Google Search Console
|
||||||
|
- [ ] Configurer Google Analytics 4
|
||||||
|
- [ ] Tester la vitesse de chargement
|
||||||
|
- [ ] Vérifier les images manquantes
|
||||||
|
- [ ] Tester les liens cassés
|
||||||
|
- [ ] Valider le markup Schema.org
|
||||||
|
|
||||||
|
#### Post-lancement
|
||||||
|
- [ ] Soumettre le sitemap à Google
|
||||||
|
- [ ] Monitorer les erreurs 404
|
||||||
|
- [ ] Analyser les mots-clés de positionnement
|
||||||
|
- [ ] Optimiser les pages avec faible CTR
|
||||||
|
- [ ] Améliorer les pages avec fort taux de rebond
|
||||||
|
|
||||||
|
### 13. Mesure de Performance
|
||||||
|
|
||||||
|
#### KPIs SEO à suivre
|
||||||
|
```yaml
|
||||||
|
Mensuel:
|
||||||
|
- Position moyenne des mots-clés
|
||||||
|
- Taux de clic (CTR)
|
||||||
|
- Pages indexées
|
||||||
|
- Erreurs de crawl
|
||||||
|
- Vitesse de chargement
|
||||||
|
|
||||||
|
Trimestriel:
|
||||||
|
- Part de marché des mots-clés
|
||||||
|
- Backlinks de qualité
|
||||||
|
- Authority Score (Domain Rating)
|
||||||
|
- Trafic organique vs objectifs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ressources Additionnelles
|
||||||
|
|
||||||
|
### Outils Français SEO
|
||||||
|
- **Google Search Console** - Monitoring principal
|
||||||
|
- **Screaming Frog** - Audit technique
|
||||||
|
- **Ahrefs/SEMrush** - Analyse concurrentielle
|
||||||
|
- **GTmetrix** - Performance
|
||||||
|
- **Schema Markup Validator** - Rich snippets
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
- [Google SEO Starter Guide - Français](https://support.google.com/webmasters/answer/7451184?hl=fr)
|
||||||
|
- [Bing Webmaster Guidelines](https://www.bing.com/webmaster/help/webmaster-guidelines-30fba23a)
|
||||||
|
- [Schema.org Documentation](https://schema.org/docs/documents.html)
|
||||||
|
|
||||||
|
### Support Communautaire
|
||||||
|
- [Webmaster Help Community - Français](https://support.google.com/webmasters/community?hl=fr)
|
||||||
|
- [Reddit r/SEO](https://www.reddit.com/r/SEO/)
|
||||||
|
- [Search Engine Journal - French](https://www.searchenginejournal.com/tag/french/)
|
||||||
|
|
||||||
|
Ce guide SEO est spécifiquement adapté au marché francophone et prend en compte les particularités linguistiques et culturelles du public cible de Mistergeek.
|
||||||
83
hugo.toml
83
hugo.toml
@@ -7,12 +7,74 @@ ignoreLogs = ["warning-goldmark-raw-html"]
|
|||||||
# [permalinks]
|
# [permalinks]
|
||||||
# posts = "/:section/:slug/"
|
# posts = "/:section/:slug/"
|
||||||
|
|
||||||
[taxonomies]
|
# [taxonomies]
|
||||||
category = "categories"
|
# category = "categories"
|
||||||
|
|
||||||
[markup.goldmark.renderer]
|
[markup.goldmark.renderer]
|
||||||
unsafe = true
|
unsafe = true
|
||||||
|
|
||||||
|
# SEO Configuration
|
||||||
|
[params.seo]
|
||||||
|
description = "Mistergeek - Tutoriels et guides en informatique et technologie en français"
|
||||||
|
keywords = ["tutoriel informatique", "guide technologie", "solutions informatiques", "développement web", "technologies", "innovation", "mistergeek"]
|
||||||
|
author = "Mistergeek"
|
||||||
|
theme_color = "#007bff"
|
||||||
|
default_image = "/assets/images/og-logo.png"
|
||||||
|
logo = "/assets/images/logo.png"
|
||||||
|
|
||||||
|
# Analytics
|
||||||
|
google_analytics = "G-XXXXXXXXXX"
|
||||||
|
google_tag_manager = "GTM-XXXXXXX"
|
||||||
|
|
||||||
|
# Social Media
|
||||||
|
[params.seo.twitter]
|
||||||
|
site = "@mistergeekfrance"
|
||||||
|
creator = "@mistergeekfrance"
|
||||||
|
|
||||||
|
# Facebook
|
||||||
|
facebook_page = "mistergeek.fr"
|
||||||
|
youtube_channel = "UCXXXXXXXXXXXXXXXXXXX"
|
||||||
|
|
||||||
|
# Search Engine Verification
|
||||||
|
# google_verification = "your-google-verification-code"
|
||||||
|
# bing_verification = "your-bing-verification-code"
|
||||||
|
# yandex_verification = "your-yandex-verification-code"
|
||||||
|
|
||||||
|
# Local SEO
|
||||||
|
[params.seo.local]
|
||||||
|
enabled = true
|
||||||
|
country = "FR"
|
||||||
|
language = "fr"
|
||||||
|
region = "Île-de-France"
|
||||||
|
city = "Paris"
|
||||||
|
latitude = "48.8566"
|
||||||
|
longitude = "2.3522"
|
||||||
|
|
||||||
|
# Rich snippets
|
||||||
|
enable_search_box = true
|
||||||
|
enable_sitelinks_searchbox = true
|
||||||
|
|
||||||
|
# Verification codes
|
||||||
|
[params.seo.verification]
|
||||||
|
google = "your-google-verification-code"
|
||||||
|
bing = "your-bing-verification-code"
|
||||||
|
yandex = "your-yandex-verification-code"
|
||||||
|
alexa = "your-alexa-verification-code"
|
||||||
|
pinterest = "your-pinterest-verification-code"
|
||||||
|
norton = "your-norton-verification-code"
|
||||||
|
|
||||||
|
# Image optimization for SEO
|
||||||
|
[imaging]
|
||||||
|
quality = 75
|
||||||
|
resampleFilter = "Lanczos"
|
||||||
|
|
||||||
|
[params.images]
|
||||||
|
# Dimensions recommandées pour le SEO
|
||||||
|
og_image = "1200x630"
|
||||||
|
twitter_image = "1200x675"
|
||||||
|
thumbnail = "400x300"
|
||||||
|
hero = "1920x1080"
|
||||||
|
|
||||||
# WordPress API Configuration
|
# WordPress API Configuration
|
||||||
[params.wordpress]
|
[params.wordpress]
|
||||||
apiUrl = "https://www.mistergeek.net/wp-json/wp/v2"
|
apiUrl = "https://www.mistergeek.net/wp-json/wp/v2"
|
||||||
@@ -31,3 +93,20 @@ ignoreLogs = ["warning-goldmark-raw-html"]
|
|||||||
[[build.cachebusters]]
|
[[build.cachebusters]]
|
||||||
source = "assets/.*\\.(css|sass|scss)$"
|
source = "assets/.*\\.(css|sass|scss)$"
|
||||||
target = "css"
|
target = "css"
|
||||||
|
|
||||||
|
# Output formats for search index
|
||||||
|
[outputs]
|
||||||
|
home = ["HTML", "RSS", "JSON"]
|
||||||
|
|
||||||
|
[outputFormats]
|
||||||
|
[outputFormats.JSON]
|
||||||
|
mediaType = "application/json"
|
||||||
|
baseName = "search-index"
|
||||||
|
isPlainText = true
|
||||||
|
notAlternative = true
|
||||||
|
|
||||||
|
# Search configuration
|
||||||
|
[params.search]
|
||||||
|
enabled = true
|
||||||
|
minQueryLength = 2
|
||||||
|
maxResults = 10
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="{{ .Site.Language.Lang | default "en" }}">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<meta name="description" content="{{ if .Description }}{{ .Description }}{{ else }}{{ .Site.Title }}{{ end }}">
|
|
||||||
<meta name="keywords" content="">
|
<!-- SEO & Social Meta Tags -->
|
||||||
<title>{{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ .Title }} - {{ .Site.Title }}{{ end }}</title>
|
<title>{{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ .Title }} - {{ .Site.Title }}{{ end }}</title>
|
||||||
<!-- Favicon -->
|
{{ partial "seo/seo-config.html" . }}
|
||||||
<link href="/assets/images/favicon.png" rel="shortcut icon">
|
|
||||||
<!-- CSS -->
|
<!-- CSS -->
|
||||||
<link href="/assets/plugins/bootstrap/bootstrap.min.css" rel="stylesheet">
|
<link href="/assets/plugins/bootstrap/bootstrap.min.css" rel="stylesheet">
|
||||||
<link href="/assets/plugins/owl-carousel/owl.carousel.min.css" rel="stylesheet">
|
<link href="/assets/plugins/owl-carousel/owl.carousel.min.css" rel="stylesheet">
|
||||||
@@ -16,73 +16,25 @@
|
|||||||
<link href="/assets/plugins/scrollcue/scrollcue.css" rel="stylesheet">
|
<link href="/assets/plugins/scrollcue/scrollcue.css" rel="stylesheet">
|
||||||
<link href="/assets/plugins/swiper/swiper-bundle.min.css" rel="stylesheet">
|
<link href="/assets/plugins/swiper/swiper-bundle.min.css" rel="stylesheet">
|
||||||
<link href="/assets/css/theme.css" rel="stylesheet">
|
<link href="/assets/css/theme.css" rel="stylesheet">
|
||||||
|
<link href="/assets/css/theme-colors/theme-color-blue.css" rel="stylesheet">
|
||||||
<!-- Fonts/Icons -->
|
<!-- Fonts/Icons -->
|
||||||
<link href="/assets/plugins/bootstrap-icons/bootstrap-icons.css" rel="stylesheet">
|
<link href="/assets/plugins/bootstrap-icons/bootstrap-icons.css" rel="stylesheet">
|
||||||
<link href="/assets/plugins/font-awesome/css/all.css" rel="stylesheet">
|
<link href="/assets/plugins/font-awesome/css/all.css" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body data-preloader="1">
|
<body data-preloader="1">
|
||||||
|
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="header center header-color-dark">
|
{{ partial "header.html" . }}
|
||||||
<div class="container">
|
|
||||||
<!-- Logo -->
|
<!-- Breadcrumb -->
|
||||||
<div class="header-logo">
|
{{ partial "breadcrumb.html" . }}
|
||||||
<h3 class="uppercase letter-spacing-1"><a href="/">{{ .Site.Title }}</a></h3>
|
|
||||||
</div>
|
|
||||||
<!-- Menu -->
|
|
||||||
<div class="header-menu">
|
|
||||||
<ul class="nav">
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="/">Home</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="/posts/">Articles</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<!-- Menu Extra -->
|
|
||||||
<div class="header-menu-extra">
|
|
||||||
<ul class="list-inline-sm">
|
|
||||||
<li><a href="#"><i class="bi bi-facebook"></i></a></li>
|
|
||||||
<li><a href="#"><i class="bi bi-twitter-x"></i></a></li>
|
|
||||||
<li><a href="#"><i class="bi bi-linkedin"></i></a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<!-- Menu Toggle -->
|
|
||||||
<button class="header-toggle">
|
|
||||||
<span></span>
|
|
||||||
</button>
|
|
||||||
</div><!-- end container -->
|
|
||||||
</div>
|
|
||||||
<!-- end Header -->
|
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
{{ block "main" . }}{{ end }}
|
{{ block "main" . }}{{ end }}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer>
|
<!-- Footer -->
|
||||||
<div class="section bg-dark">
|
{{ partial "footer.html" . }}
|
||||||
<div class="container">
|
|
||||||
<div class="row g-4 align-items-center">
|
|
||||||
<div class="col-12 col-md-6 text-center text-md-start">
|
|
||||||
<ul class="list-inline-dash">
|
|
||||||
<li><a href="/faq/">FAQ</a></li>
|
|
||||||
<li><a href="/careers/">Careers</a></li>
|
|
||||||
<li><a href="/clients/">Clients</a></li>
|
|
||||||
</ul>
|
|
||||||
<p class="mt-2">© {{ now.Format "2006" }} {{ .Site.Title }}, All Rights Reserved.</p>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-6 text-center text-md-end">
|
|
||||||
<ul class="list-inline">
|
|
||||||
<li><a href="#"><i class="bi bi-facebook"></i></a></li>
|
|
||||||
<li><a href="#"><i class="bi bi-twitter-x"></i></a></li>
|
|
||||||
<li><a href="#"><i class="bi bi-pinterest"></i></a></li>
|
|
||||||
<li><a href="#"><i class="bi bi-instagram"></i></a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div><!-- end row -->
|
|
||||||
</div><!-- end container -->
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
<!-- Scroll to top button -->
|
<!-- Scroll to top button -->
|
||||||
<div class="scrolltotop icon-lg">
|
<div class="scrolltotop icon-lg">
|
||||||
@@ -94,5 +46,6 @@
|
|||||||
<script src="/assets/plugins/jquery.min.js"></script>
|
<script src="/assets/plugins/jquery.min.js"></script>
|
||||||
<script src="/assets/plugins/plugins.js"></script>
|
<script src="/assets/plugins/plugins.js"></script>
|
||||||
<script src="/assets/js/functions.js"></script>
|
<script src="/assets/js/functions.js"></script>
|
||||||
|
{{ partial "scripts.html" . }}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -1,21 +1,63 @@
|
|||||||
{{ define "main" }}
|
{{ define "main" }}
|
||||||
<div class="posts-list">
|
|
||||||
<h1>{{ .Title }}</h1>
|
|
||||||
|
|
||||||
{{ range .Pages }}
|
<!-- layouts/_default/list.html -->
|
||||||
<article class="post-preview">
|
<div class="section-sm bg-gray-lighter">
|
||||||
<h2><a href="{{ .RelPermalink }}">{{ .Title }}</a></h2>
|
<div class="container text-center">
|
||||||
<div class="post-meta">
|
<h1 class="font-family-playfair">{{ .Site.Title }}</h1>
|
||||||
<time>{{ .Date.Format "Jan 2, 2006" }}</time>
|
</div><!-- end container -->
|
||||||
{{ if .Params.author }}
|
|
||||||
<span>{{ .Params.author }}</span>
|
|
||||||
{{ end }}
|
|
||||||
</div>
|
|
||||||
{{ if .Params.excerpt }}
|
|
||||||
<p class="post-excerpt">{{ .Params.excerpt }}</p>
|
|
||||||
{{ end }}
|
|
||||||
<a href="{{ .RelPermalink }}" class="read-more">Read more →</a>
|
|
||||||
</article>
|
|
||||||
{{ end }}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Blog section -->
|
||||||
|
<div class="section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row g-4">
|
||||||
|
<div class="col-12 col-sm-10 offset-sm-1 col-md-8 offset-md-2">
|
||||||
|
{{ $paginationLimit := 10 }}
|
||||||
|
{{ if .Site.Params.paginationLimit }}{{ $paginationLimit = .Site.Params.paginationLimit }}{{ end }}
|
||||||
|
{{ $paginator := .Paginator $paginationLimit }}
|
||||||
|
{{ range $paginator.Pages }}
|
||||||
|
<!-- Blog Post box -->
|
||||||
|
<div class="mb-5">
|
||||||
|
<div class="img-link-box">
|
||||||
|
<a href="{{ .RelPermalink }}">
|
||||||
|
{{ if .Params.featured_image }}
|
||||||
|
<img src="{{ .Params.featured_image }}" alt="{{ .Title }}">
|
||||||
|
{{ else }}
|
||||||
|
<img src="/assets/images/col-1.jpg" alt="{{ .Title }}">
|
||||||
|
{{ end }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="mt-4">
|
||||||
|
<div class="d-flex justify-content-between mb-2">
|
||||||
|
<!-- Display category -->
|
||||||
|
{{ partial "categories-first.html" . }}
|
||||||
|
|
||||||
|
<div class="d-inline-flex">
|
||||||
|
<span class="font-small">{{ .Date.Format "02/07/2006" }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h2><a class="text-link-1" href="{{ .RelPermalink }}">{{ .Title }}</a></h2>
|
||||||
|
{{ if .Params.excerpt }}
|
||||||
|
<p>{{ .Params.excerpt }}</p>
|
||||||
|
{{ else if .Summary }}
|
||||||
|
<p>{{ .Summary }}</p>
|
||||||
|
{{ else }}
|
||||||
|
<p>{{ truncate 200 .Content }}</p>
|
||||||
|
{{ end }}
|
||||||
|
<div class="mt-3">
|
||||||
|
<a class="button-text-1" href="{{ .RelPermalink }}">Lire la suite</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- End Blog Post box -->
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<!-- Pagination -->
|
||||||
|
{{ partial "pagination.html" (dict "Paginator" .Paginator "Page" .) }}
|
||||||
|
</div>
|
||||||
|
</div><!-- end row -->
|
||||||
|
</div><!-- end container -->
|
||||||
|
</div>
|
||||||
|
<!-- end Blog section -->
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
|||||||
@@ -1,38 +1,131 @@
|
|||||||
{{ define "main" }}
|
{{ define "main" }}
|
||||||
|
<div class="section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row g-4">
|
||||||
|
<div class="col-12 col-md-10 offset-md-1">
|
||||||
|
<h1 class="fw-normal">{{ .Title }}</h1>
|
||||||
|
<ul class="list-inline-dash">
|
||||||
|
{{ if .Params.author }}
|
||||||
|
<li>Par <a href="/author/{{ .Params.author | anchorize }}">{{ .Params.author }}</a></li>
|
||||||
|
{{ end }}
|
||||||
|
{{ with .Params.categories }}
|
||||||
|
{{ range . }}
|
||||||
|
<li>dans <a href="/{{ . | anchorize }}">{{ . }}</a></li>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
<li> le {{ .Date.Format "02/01/2006" }}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div><!-- end row -->
|
||||||
|
</div><!-- end container -->
|
||||||
|
</div>
|
||||||
|
|
||||||
<article class="post">
|
<!-- Featured Image -->
|
||||||
<header class="post-header">
|
{{ if .Params.featured_image }}
|
||||||
<h1 class="post-title">{{ .Title }}</h1>
|
<div class="container">
|
||||||
<div class="post-meta">
|
<div class="p-2 text-center">
|
||||||
<time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "January 2, 2006" }}</time>
|
<img src="{{ .Params.featured_image }}" alt="{{ .Title }}" class="mx-auto d-block">
|
||||||
{{ if .Params.author }}
|
|
||||||
<span class="post-author">{{ .Params.author }}</span>
|
|
||||||
{{ end }}
|
|
||||||
<div class="post-categories">
|
|
||||||
{{ with .Params.categories }}
|
|
||||||
{{ range . }}
|
|
||||||
<a href="/categories/{{ . | urlize }}" class="category">{{ . }}</a>
|
|
||||||
{{ end }}
|
|
||||||
{{ else }}
|
|
||||||
<a href="/categories/non-classe" class="category">Non classé</a>
|
|
||||||
{{ end }}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{{ if .Params.featured_image }}
|
</div><!-- end container -->
|
||||||
<img src="{{ .Params.featured_image }}" alt="{{ .Title }}" class="featured-image">
|
{{ end }}
|
||||||
{{ end }}
|
<!-- end Featured Image -->
|
||||||
</header>
|
|
||||||
|
<!-- Post Content -->
|
||||||
<div class="post-content">
|
<div class="section">
|
||||||
{{ .Content }}
|
<div class="container">
|
||||||
</div>
|
<div class="row g-4">
|
||||||
|
<div class="col-12 col-md-10 offset-md-1">
|
||||||
{{ if .Params.tags }}
|
<div class="post-content">
|
||||||
<div class="post-tags">
|
{{ .Content }}
|
||||||
{{ range .Params.tags }}
|
</div>
|
||||||
<a href="/tags/{{ . | urlize }}" class="tag">#{{ . }}</a>
|
</div>
|
||||||
{{ end }}
|
</div><!-- end row -->
|
||||||
</div>
|
</div><!-- end container -->
|
||||||
{{ end }}
|
</div>
|
||||||
</article>
|
<!-- end Post Content -->
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
<!-- Tags and Share -->
|
||||||
|
<div class="section-xs border-top">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row g-4">
|
||||||
|
{{ if .Params.tags }}
|
||||||
|
<div class="col-6">
|
||||||
|
<h6 class="font-small fw-medium uppercase">Tags</h6>
|
||||||
|
<ul class="list-inline-sm">
|
||||||
|
{{ range .Params.tags }}
|
||||||
|
<li><a href="/tags/{{ . | anchorize }}">{{ . }}</a></li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
<div class="col-6 text-end">
|
||||||
|
<h6 class="font-small fw-medium uppercase">Share On</h6>
|
||||||
|
<ul class="list-inline">
|
||||||
|
<li><a href="#"><i class="bi bi-facebook"></i></a></li>
|
||||||
|
<li><a href="#"><i class="bi bi-twitter-x"></i></a></li>
|
||||||
|
<li><a href="#"><i class="bi bi-google"></i></a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div><!-- end row -->
|
||||||
|
</div><!-- end container -->
|
||||||
|
</div>
|
||||||
|
*/}}
|
||||||
|
|
||||||
|
<!-- Comments section -->
|
||||||
|
{{ if .Site.Params.comments.enable }}
|
||||||
|
<div class="section bg-gray-lighter">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row g-4">
|
||||||
|
<div class="col-12 col-md-10 offset-md-1">
|
||||||
|
<h4 class="fw-normal mb-5">Comments</h4>
|
||||||
|
|
||||||
|
{{ if .Site.Params.comments.disqus }}
|
||||||
|
<div id="disqus_thread"></div>
|
||||||
|
<script>
|
||||||
|
var disqus_config = function () {
|
||||||
|
this.page.url = "{{ .Permalink }}";
|
||||||
|
this.page.identifier = "{{ .RelPermalink }}";
|
||||||
|
};
|
||||||
|
(function() {
|
||||||
|
var d = document, s = d.createElement('script');
|
||||||
|
s.src = 'https://{{ .Site.Params.comments.disqus }}.disqus.com/embed.js';
|
||||||
|
s.setAttribute('data-timestamp', +new Date());
|
||||||
|
(d.head || d.body).appendChild(s);
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
{{ else }}
|
||||||
|
<p>Comments are disabled.</p>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div><!-- end row -->
|
||||||
|
</div><!-- end container -->
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<!-- Write Comment section -->
|
||||||
|
{{ if .Site.Params.comments.enable }}
|
||||||
|
<div class="section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row g-4">
|
||||||
|
<div class="col-12 col-md-10 offset-md-1">
|
||||||
|
<h4 class="fw-normal mb-5">Write a Comment</h4>
|
||||||
|
<form id="comment-form">
|
||||||
|
<div class="row g-4">
|
||||||
|
<div class="col-12 col-sm-6">
|
||||||
|
<input type="text" name="name" placeholder="Name" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-sm-6">
|
||||||
|
<input type="email" name="email" placeholder="E-Mail" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<textarea name="message" placeholder="Message"></textarea>
|
||||||
|
<button class="button button-lg button-outline-gray" type="submit">Post Comment</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div><!-- end row -->
|
||||||
|
</div><!-- end container -->
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
161
layouts/author/index.html.old
Normal file
161
layouts/author/index.html.old
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
{{ define "main" }}
|
||||||
|
<!-- Page Header -->
|
||||||
|
<div class="section-sm bg-gray-lighter">
|
||||||
|
<div class="container text-center">
|
||||||
|
<h3 class="font-family-playfair">Authors</h3>
|
||||||
|
<p class="mt-2">Discover all our talented authors and their contributions</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Authors Section -->
|
||||||
|
<div class="section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row g-4">
|
||||||
|
{{ $authors := slice }}
|
||||||
|
{{ $authorMap := dict }}
|
||||||
|
|
||||||
|
<!-- Collect all unique authors -->
|
||||||
|
{{ range .Site.RegularPages }}
|
||||||
|
{{ if .Params.author }}
|
||||||
|
{{ $authorName := .Params.author }}
|
||||||
|
{{ $authorSlug := .Params.author_slug | default ($authorName | urlize) }}
|
||||||
|
|
||||||
|
<!-- Check if author already exists -->
|
||||||
|
{{ $existing := false }}
|
||||||
|
{{ range $authors }}
|
||||||
|
{{ if eq .name $authorName }}
|
||||||
|
{{ $existing = true }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<!-- Add new author if not exists -->
|
||||||
|
{{ if not $existing }}
|
||||||
|
{{ $author := dict
|
||||||
|
"name" $authorName
|
||||||
|
"slug" $authorSlug
|
||||||
|
"bio" .Params.author_bio
|
||||||
|
"photo" .Params.author_photo
|
||||||
|
"website" .Params.author_website
|
||||||
|
"twitter" .Params.author_twitter
|
||||||
|
"linkedin" .Params.author_linkedin
|
||||||
|
"posts" (slice)
|
||||||
|
}}
|
||||||
|
{{ $authors = $authors | append $author }}
|
||||||
|
{{ $authorMap = merge $authorMap (dict $authorName $author) }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<!-- Add post to author's posts -->
|
||||||
|
{{ if $author := index $authorMap $authorName }}
|
||||||
|
{{ $post := dict
|
||||||
|
"title" .Title
|
||||||
|
"permalink" .Permalink
|
||||||
|
}}
|
||||||
|
{{ $author := merge $author (dict "posts" ($author.posts | append $post)) }}
|
||||||
|
{{ $authorMap = merge $authorMap (dict $authorName $author) }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<!-- Display authors -->
|
||||||
|
{{ if gt (len $authors) 0 }}
|
||||||
|
{{ range $authors }}
|
||||||
|
<div class="col-12 col-md-6 col-lg-4">
|
||||||
|
<div class="team-box team-box-style-2">
|
||||||
|
<div class="team-box-img">
|
||||||
|
{{ if .photo }}
|
||||||
|
<img src="{{ .photo }}" alt="{{ .name }}">
|
||||||
|
{{ else }}
|
||||||
|
<img src="/assets/images/avatar-placeholder.jpg" alt="{{ .name }}">
|
||||||
|
{{ end }}
|
||||||
|
<div class="team-box-content">
|
||||||
|
<h5 class="font-family-poppins">{{ .name }}</h5>
|
||||||
|
{{ if .bio }}
|
||||||
|
<p class="font-small">{{ .bio | truncate 120 }}</p>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<div class="team-box-social">
|
||||||
|
{{ if .website }}
|
||||||
|
<a href="{{ .website }}" target="_blank" rel="noopener noreferrer">
|
||||||
|
<i class="fas fa-globe"></i>
|
||||||
|
</a>
|
||||||
|
{{ end }}
|
||||||
|
{{ if .twitter }}
|
||||||
|
<a href="https://twitter.com/{{ .twitter }}" target="_blank" rel="noopener noreferrer">
|
||||||
|
<i class="fab fa-twitter"></i>
|
||||||
|
</a>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
{{ if .linkedin }}
|
||||||
|
<a href="https://linkedin.com/in/{{ .linkedin }}" target="_blank" rel="noopener noreferrer">
|
||||||
|
<i class="fab fa-linkedin-in"></i>
|
||||||
|
</a>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-3">
|
||||||
|
<a href="/author/{{ .slug }}" class="button-text-1">
|
||||||
|
{{ len .posts }} {{ if eq (len .posts) 1 }}Article{{ else }}Articles{{ end }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
{{ else }}
|
||||||
|
<div class="col-12 text-center py-5">
|
||||||
|
<h4>No authors found</h4>
|
||||||
|
<p>No authors have been added yet. Check back soon!</p>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Additional Author Information Section -->
|
||||||
|
{{ if gt (len $authors) 0 }}
|
||||||
|
<div class="section bg-gray-lighter">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 text-center">
|
||||||
|
<h4 class="font-family-playfair mb-4">Our Authors</h4>
|
||||||
|
<p class="max-width-600 mx-auto">
|
||||||
|
Meet the talented writers and contributors who bring you insightful content across various topics.
|
||||||
|
Each author brings their unique perspective and expertise to create valuable content for our readers.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mt-5">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="row g-3">
|
||||||
|
{{ range $authors }}
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<div class="d-flex align-items-center p-3 bg-white rounded">
|
||||||
|
<div class="flex-shrink-0">
|
||||||
|
{{ if .photo }}
|
||||||
|
<img src="{{ .photo }}" alt="{{ .name }}" class="rounded-circle" width="60" height="60">
|
||||||
|
{{ else }}
|
||||||
|
<div class="bg-gray rounded-circle d-flex align-items-center justify-content-center" style="width: 60px; height: 60px;">
|
||||||
|
<i class="fas fa-user"></i>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
<div class="flex-grow-1 ms-3">
|
||||||
|
<h6 class="mb-1">{{ .name }}</h6>
|
||||||
|
<small class="text-muted">{{ len .posts }} {{ if eq (len .posts) 1 }}publication{{ else }}publications{{ end }}</small>
|
||||||
|
</div>
|
||||||
|
<div class="flex-shrink-0">
|
||||||
|
<a href="/author/{{ .slug }}" class="btn btn-sm btn-outline-primary">
|
||||||
|
<i class="fas fa-arrow-right"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
49
layouts/author/list.html
Normal file
49
layouts/author/list.html
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
{{ define "main" }}
|
||||||
|
{{ $authors := site.Data.wordpress.authors }}
|
||||||
|
|
||||||
|
<div class="section-sm bg-gray-lighter">
|
||||||
|
<div class="container text-center">
|
||||||
|
<h3 class="font-family-playfair">{{ .Title }}</h3>
|
||||||
|
<p class="mt-2">Liste de tous les auteurs</p>
|
||||||
|
</div><!-- end container -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="author-list">
|
||||||
|
<div class="row">
|
||||||
|
{{ range $author := $authors }}
|
||||||
|
<div class="col-md-6 mb-4">
|
||||||
|
<article class="author-card-item h-100 card" data-author-name="{{ $author.name }}">
|
||||||
|
<div class="row g-0 align-items-center">
|
||||||
|
<div class="author-card-image col-md-4 p-2">
|
||||||
|
{{ with index $author.avatar_urls "96" }}
|
||||||
|
<img decoding="async" src="{{ . }}" alt="{{ $author.name }}" class="author-image img-fluid" loading="lazy">
|
||||||
|
{{ else }}
|
||||||
|
<img decoding="async" src="/assets/images/img-avatar-md@2x.jpg" alt="{{ $author.name }}" class="author-image img-fluid" loading="lazy">
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
<div class="author-card-info col-md-8">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title author-card-name">{{ $author.name }}</h5>
|
||||||
|
{{ with $author.description }}
|
||||||
|
<p class="card-text author-card-title">
|
||||||
|
<small class="text-muted author-card-title-text">{{ . }}</small>
|
||||||
|
</p>
|
||||||
|
{{ end }}
|
||||||
|
<div class="author-card-buttons mt-3">
|
||||||
|
<div class="author-card-button">
|
||||||
|
<a href="/author/{{ $author.slug | anchorize }}" class="author-button-link" aria-label="Voir les articles de {{ $author.name }}">Voir les articles<svg class="uikit-icon" style="width: 1em;" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="var(--c-svg, currentColor)" d="M7.5 4.5V6h9.442L4.5 18.442 5.558 19.5 18 7.058V16.5h1.5v-12z"></path></svg></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div><!-- end container -->
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
34
layouts/categories/list.html
Normal file
34
layouts/categories/list.html
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{{ define "main" }}
|
||||||
|
{{ $defaultCategory := "General" }}
|
||||||
|
{{ if .Site.Params.defaultCategory }}{{ $defaultCategory = .Site.Params.defaultCategory }}{{ end }}
|
||||||
|
|
||||||
|
<div class="section-sm bg-gray-lighter">
|
||||||
|
<div class="container text-center">
|
||||||
|
<h1 class="font-family-playfair">{{ .Title }}</h1>
|
||||||
|
<p class="mt-3">Cette page répertorie toutes les catégories de notre site, offrant une vue d'ensemble structurée de notre contenu.</p>
|
||||||
|
</div><!-- end container -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Blog section -->
|
||||||
|
<div class="section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row g-4">
|
||||||
|
<div class="col-12 col-sm-10 offset-sm-1 col-md-8 offset-md-2">
|
||||||
|
{{ range $.Site.Data.wordpress.categories }}
|
||||||
|
<div class="mb-5">
|
||||||
|
<div class="mt-4">
|
||||||
|
<h2><a class="text-link-1" href="{{ .link | relURL }}">{{ .name }}</a></h2>
|
||||||
|
<p>{{ .description | safeHTML }}</p>
|
||||||
|
<div class="mt-3">
|
||||||
|
<a class="button-text-1" href="{{ .link | relURL }}">Voir la catégorie</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ if not .IsLast }}<hr class="my-5">{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
</div><!-- end row -->
|
||||||
|
</div><!-- end container -->
|
||||||
|
</div>
|
||||||
|
<!-- end Blog section -->
|
||||||
|
{{ end }}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
{{ define "main" }}
|
{{ define "main" }}
|
||||||
{{ $defaultCategory := "General" }}
|
|
||||||
{{ if .Site.Params.defaultCategory }}{{ $defaultCategory = .Site.Params.defaultCategory }}{{ end }}
|
<!-- layouts/index.html -->
|
||||||
<div class="section-sm bg-gray-lighter">
|
<div class="section-sm bg-gray-lighter">
|
||||||
<div class="container text-center">
|
<div class="container text-center">
|
||||||
<h3 class="font-family-playfair">{{ .Site.Title }} blog.</h3>
|
<h1 class="font-family-playfair">{{ .Site.Title }}</h1>
|
||||||
</div><!-- end container -->
|
<p class="mt-3">{{ .Site.Params.seo.description }}</p>
|
||||||
|
</div><!-- end container -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Blog section -->
|
<!-- Blog section -->
|
||||||
@@ -27,31 +28,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<div class="d-flex justify-content-between mb-2">
|
<div class="d-flex justify-content-between mb-2">
|
||||||
<div class="d-inline-flex">
|
<!-- Display category -->
|
||||||
{{ if .Params.categories }}
|
{{ partial "categories-first.html" . }}
|
||||||
{{ range $index, $category := .Params.categories }}
|
|
||||||
{{ if $index }}, {{ end }}
|
|
||||||
{{ if and (eq (printf "%T" $category) "string") }}
|
|
||||||
<a class="font-family-poppins font-small fw-medium uppercase" href="/categories/{{ $category | urlize }}">{{ $category }}</a>
|
|
||||||
{{ else if and (eq (printf "%T" $category) "map") }}
|
|
||||||
{{ if $category.name }}
|
|
||||||
<a class="font-family-poppins font-small fw-medium uppercase" href="/categories/{{ $category.name | urlize }}">{{ $category.name }}</a>
|
|
||||||
{{ else }}
|
|
||||||
<a class="font-family-poppins font-small fw-medium uppercase" href="#">{{ $defaultCategory }}</a>
|
|
||||||
{{ end }}
|
|
||||||
{{ else }}
|
|
||||||
<a class="font-family-poppins font-small fw-medium uppercase" href="#">{{ $defaultCategory }}</a>
|
|
||||||
{{ end }}
|
|
||||||
{{ end }}
|
|
||||||
{{ else }}
|
|
||||||
<a class="font-family-poppins font-small fw-medium uppercase" href="#">{{ $defaultCategory }}</a>
|
|
||||||
{{ end }}
|
|
||||||
</div>
|
|
||||||
<div class="d-inline-flex">
|
<div class="d-inline-flex">
|
||||||
<span class="font-small">{{ .Date.Format "02/07/2006" }}</span>
|
<span class="font-small">{{ .Date.Format "02/07/2006" }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h4><a class="text-link-1" href="{{ .Permalink }}">{{ .Title }}</a></h4>
|
<h2><a class="text-link-1" href="{{ .Permalink }}">{{ .Title }}</a></h2>
|
||||||
{{ if .Params.excerpt }}
|
{{ if .Params.excerpt }}
|
||||||
<p>{{ .Params.excerpt }}</p>
|
<p>{{ .Params.excerpt }}</p>
|
||||||
{{ else if .Summary }}
|
{{ else if .Summary }}
|
||||||
@@ -60,7 +44,7 @@
|
|||||||
<p>{{ truncate 200 .Content }}</p>
|
<p>{{ truncate 200 .Content }}</p>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
<a class="button-text-1" href="{{ .Permalink }}">Read More</a>
|
<a class="button-text-1" href="{{ .Permalink }}">Lire la suite</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
56
layouts/pages/list.html
Normal file
56
layouts/pages/list.html
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
{{ define "main" }}
|
||||||
|
{{ $authorName := .Params.author }}
|
||||||
|
{{ $authorSlug := .Params.author_slug }}
|
||||||
|
{{ $authorPosts := where .Site.RegularPages "Params.author" $authorName }}
|
||||||
|
|
||||||
|
<div class="section-sm bg-gray-lighter">
|
||||||
|
<div class="container text-center">
|
||||||
|
<h1 class="font-family-playfair">{{ .Title }}</h1>
|
||||||
|
<p class="mt-2">Retrouvez toutes les pages utiles du site ici.</p>
|
||||||
|
</div><!-- end container -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Blog section -->
|
||||||
|
<div class="section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row g-4">
|
||||||
|
<div class="col-12 col-sm-10 offset-sm-1 col-md-8 offset-md-2">
|
||||||
|
{{ $paginationLimit := 10 }}
|
||||||
|
{{ if .Site.Params.paginationLimit }}{{ $paginationLimit = .Site.Params.paginationLimit }}{{ end }}
|
||||||
|
{{ $paginator := .Paginate $authorPosts $paginationLimit }}
|
||||||
|
|
||||||
|
{{ if gt (len $authorPosts) 0 }}
|
||||||
|
{{ range $paginator.Pages }}
|
||||||
|
<!-- Blog Post box -->
|
||||||
|
<div class="mb-5">
|
||||||
|
<div class="mt-4">
|
||||||
|
<h2><a class="text-link-1" href="{{ .RelPermalink }}">{{ .Title }}</a></h2>
|
||||||
|
{{ if .Params.excerpt }}
|
||||||
|
<p>{{ .Params.excerpt }}</p>
|
||||||
|
{{ else if .Summary }}
|
||||||
|
<p>{{ .Summary }}</p>
|
||||||
|
{{ else }}
|
||||||
|
<p>{{ truncate 200 .Content }}</p>
|
||||||
|
{{ end }}
|
||||||
|
<div class="mt-3">
|
||||||
|
<a class="button-text-1" href="{{ .RelPermalink }}">Lire la suite</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- End Blog Post box -->
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<!-- Pagination -->
|
||||||
|
{{ partial "pagination.html" (dict "Paginator" .Paginator "Page" .) }}
|
||||||
|
{{ else }}
|
||||||
|
<div class="text-center py-5">
|
||||||
|
<h4>Aucun article trouvé</h4>
|
||||||
|
<p>Aucun article n'a été trouvé pour cet auteur.</p>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
</div><!-- end row -->
|
||||||
|
</div><!-- end container -->
|
||||||
|
</div>
|
||||||
|
<!-- end Blog section -->
|
||||||
|
{{ end }}
|
||||||
70
layouts/partials/breadcrumb.html
Normal file
70
layouts/partials/breadcrumb.html
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<!-- Breadcrumb Navigation with SEO optimization -->
|
||||||
|
<div class="breadcrumb-wrapper">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<nav aria-label="breadcrumb">
|
||||||
|
<ol class="breadcrumb" itemscope itemtype="https://schema.org/BreadcrumbList">
|
||||||
|
|
||||||
|
<!-- Home/Accueil with SEO -->
|
||||||
|
<li class="breadcrumb-item" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
|
||||||
|
<a href="{{ "/" | relLangURL }}" itemprop="item" title="{{ i18n "home" | default "Accueil" }}">
|
||||||
|
<span itemprop="name">{{ i18n "home" | default "Accueil" }}</span>
|
||||||
|
</a>
|
||||||
|
<meta itemprop="position" content="1" />
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<!-- Category with SEO and linking -->
|
||||||
|
{{ if or .Params.categories .Section }}
|
||||||
|
{{ $category := "" }}
|
||||||
|
{{ $categorySlug := "" }}
|
||||||
|
{{ $categoryUrl := "" }}
|
||||||
|
|
||||||
|
{{ if .Params.categories }}
|
||||||
|
{{ $category = index .Params.categories 0 }}
|
||||||
|
{{ $categorySlug = anchorize $category }}
|
||||||
|
{{ $categoryUrl = printf "/categories/%s" $categorySlug }}
|
||||||
|
{{ else if .Section }}
|
||||||
|
{{ $category = humanize .Section }}
|
||||||
|
{{ $categorySlug = .Section }}
|
||||||
|
{{ $categoryUrl = printf "/%s" .Section }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ if $category }}
|
||||||
|
<li class="breadcrumb-item active" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem" aria-current="page">
|
||||||
|
<a href="{{ $categoryUrl | relLangURL }}" itemprop="item" title="{{ $category }}">
|
||||||
|
<span itemprop="name">{{ $category }}</span>
|
||||||
|
</a>
|
||||||
|
<meta itemprop="position" content="2" />
|
||||||
|
</li>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- JSON-LD Structured Data for SEO -->
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "BreadcrumbList",
|
||||||
|
"itemListElement": [
|
||||||
|
{
|
||||||
|
"@type": "ListItem",
|
||||||
|
"position": 1,
|
||||||
|
"name": "{{ i18n "home" | default "Accueil" }}",
|
||||||
|
"item": "{{ "/" | absLangURL }}"
|
||||||
|
}{{ if or .Params.categories .Section }},
|
||||||
|
{
|
||||||
|
"@type": "ListItem",
|
||||||
|
"position": 2,
|
||||||
|
"name": "{{ if .Params.categories }}{{ index .Params.categories 0 }}{{ else }}{{ humanize .Section }}{{ end }}",
|
||||||
|
"item": "{{ if .Params.categories }}{{ printf "%s/categories/%s" (absLangURL "") (anchorize (index .Params.categories 0)) }}{{ else }}{{ printf "%s/%s" (absLangURL "") .Section }}{{ end }}"
|
||||||
|
}{{ end }}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</script>
|
||||||
13
layouts/partials/categories-first.html
Normal file
13
layouts/partials/categories-first.html
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<div class="d-inline-flex">
|
||||||
|
{{ if .Params.categories }}
|
||||||
|
{{ with index .Params.categories 0 }}
|
||||||
|
{{ if and (eq (printf "%T" .) "string") }}
|
||||||
|
<a class="font-family-poppins font-small fw-medium uppercase" href="/{{ . | anchorize }}">{{ . }}</a>
|
||||||
|
{{ else if and (eq (printf "%T" .) "map") }}
|
||||||
|
{{ if .name }}
|
||||||
|
<a class="font-family-poppins font-small fw-medium uppercase" href="/{{ .name | anchorize }}">{{ .name }}</a>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
71
layouts/partials/footer.html
Normal file
71
layouts/partials/footer.html
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<footer>
|
||||||
|
<div class="section-sm bg-dark">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row g-4">
|
||||||
|
<div class="col-6 col-sm-6 col-lg-3">
|
||||||
|
<h3 class="uppercase letter-spacing-1">{{ .Site.Title }}</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-6 col-sm-6 col-lg-3">
|
||||||
|
<h6 class="font-small fw-medium uppercase">Pages</h6>
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
<li><a href="/">Accueil</a></li>
|
||||||
|
{{ if .Site.Data.wordpress }}
|
||||||
|
{{ range $index, $element := .Site.Data.wordpress.navigation }}
|
||||||
|
<li>
|
||||||
|
<a href="/pages/{{ $element.slug }}">{{ $element.title }}</a>
|
||||||
|
</li>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="col-6 col-sm-6 col-lg-3">
|
||||||
|
<h6 class="font-small fw-medium uppercase">Toutes les catégories</h6>
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
{{ if .Site.Data.wordpress }}
|
||||||
|
{{ $count := 0 }}
|
||||||
|
{{ range $index, $element := .Site.Data.wordpress.categories }}
|
||||||
|
{{ if ne $element.name "Featured" }}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/{{ $element.slug }}">{{ $element.name }}</a>
|
||||||
|
</li>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
<div class="col-6 col-sm-6 col-lg-3">
|
||||||
|
<h6 class="font-small fw-medium uppercase">Contact Info</h6>
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
<li>121 King St, Melbourne VIC 3000</li>
|
||||||
|
<li>contact@example.com</li>
|
||||||
|
<li>+(123) 456 789 01</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
*/}}
|
||||||
|
|
||||||
|
</div><!-- end row -->
|
||||||
|
</div><!-- end container -->
|
||||||
|
</div>
|
||||||
|
<div class="bg-black py-4">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row align-items-center g-2 g-lg-3">
|
||||||
|
<div class="col-12 col-md-6 text-center text-md-start">
|
||||||
|
<p>© {{ now.Format "2006" }} {{ .Site.Title }}, All Rights Reserved.</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6 text-center text-md-end">
|
||||||
|
<ul class="list-inline-sm">
|
||||||
|
<li><a class="button-circle button-circle-sm button-circle-social-facebook" href="https://www.facebook.com/mistergeekfrance"><i class="bi bi-facebook"></i></a></li>
|
||||||
|
<!-- <li><a class="button-circle button-circle-sm button-circle-social-twitter" href="#"><i class="bi bi-twitter-x"></i></a></li> -->
|
||||||
|
<!-- <li><a class="button-circle button-circle-sm button-circle-social-pinterest" href="#"><i class="bi bi-pinterest"></i></a></li> -->
|
||||||
|
<!-- <li><a class="button-circle button-circle-sm button-circle-social-instagram" href="#"><i class="bi bi-instagram"></i></a></li> -->
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div><!-- end row -->
|
||||||
|
</div><!-- end container -->
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
46
layouts/partials/header.html
Normal file
46
layouts/partials/header.html
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<!-- Header -->
|
||||||
|
<div class="header center header-color-dark">
|
||||||
|
<div class="container">
|
||||||
|
<!-- Logo -->
|
||||||
|
<div class="header-logo">
|
||||||
|
<h3 class="uppercase letter-spacing-1"><a href="/">{{ .Site.Title }}</a></h3>
|
||||||
|
</div>
|
||||||
|
<!-- Menu -->
|
||||||
|
<div class="header-menu">
|
||||||
|
<ul class="nav">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/">Accueil</a>
|
||||||
|
</li>
|
||||||
|
{{ if .Site.Data.wordpress }}
|
||||||
|
{{ $count := 0 }}
|
||||||
|
{{ range $index, $element := .Site.Data.wordpress.categories }}
|
||||||
|
{{ if or (eq $element.name "Featured") (eq $element.name "Non classé") }}
|
||||||
|
{{ continue }}
|
||||||
|
{{ else if lt $count 5 }}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/{{ $element.slug }}">{{ $element.name }}</a>
|
||||||
|
</li>
|
||||||
|
{{ $count = add $count 1 }}
|
||||||
|
{{ else }}
|
||||||
|
{{ break }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<!-- Menu Extra -->
|
||||||
|
{{/* <div class="header-menu-extra">
|
||||||
|
<ul class="list-inline-sm">
|
||||||
|
<li><a href="#"><i class="bi bi-facebook"></i></a></li>
|
||||||
|
<li><a href="#"><i class="bi bi-twitter-x"></i></a></li>
|
||||||
|
<li><a href="#"><i class="bi bi-linkedin"></i></a></li>
|
||||||
|
</ul>
|
||||||
|
</div> */}}
|
||||||
|
<!-- Menu Toggle -->
|
||||||
|
<button class="header-toggle">
|
||||||
|
<span></span>
|
||||||
|
</button>
|
||||||
|
</div><!-- end container -->
|
||||||
|
</div>
|
||||||
|
<!-- end Header -->
|
||||||
|
|
||||||
@@ -1,23 +1,25 @@
|
|||||||
<!-- Pagination -->
|
<!-- Pagination -->
|
||||||
{{ if gt (len .Site.RegularPages) 10 }}
|
{{ $paginator := .Paginator }}
|
||||||
|
{{ $page := .Page }}
|
||||||
|
{{ if gt $paginator.TotalPages 1 }}
|
||||||
<nav>
|
<nav>
|
||||||
<ul class="pagination justify-content-center mt-5">
|
<ul class="pagination justify-content-center mt-5">
|
||||||
<!-- Previous -->
|
<!-- Previous -->
|
||||||
<li class="page-item {{ if not .Paginator.HasPrev }}disabled{{ end }}">
|
<li class="page-item {{ if not $paginator.HasPrev }}disabled{{ end }}">
|
||||||
{{ if .Paginator.HasPrev }}
|
{{ if $paginator.HasPrev }}
|
||||||
<a class="page-link" href="{{ .Paginator.Prev.URL }}">« Previous</a>
|
<a class="page-link" href="{{ $paginator.Prev.URL }}">« Précédent</a>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<a class="page-link" href="#">« Previous</a>
|
<a class="page-link" href="#">« Précédent</a>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<!-- Page numbers -->
|
<!-- Page numbers -->
|
||||||
{{ $currentPage := .Paginator.PageNumber }}
|
{{ $currentPage := $paginator.PageNumber }}
|
||||||
{{ $totalPages := .Paginator.TotalPages }}
|
{{ $totalPages := $paginator.TotalPages }}
|
||||||
|
|
||||||
<!-- Always show first page -->
|
<!-- Always show first page -->
|
||||||
<li class="page-item {{ if eq $currentPage 1 }}active{{ end }}">
|
<li class="page-item {{ if eq $currentPage 1 }}active{{ end }}">
|
||||||
<a class="page-link" href="{{ .Paginator.First.URL }}">1</a>
|
<a class="page-link" href="{{ $paginator.First.URL }}">1</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<!-- Show ellipsis if current page is > 4 -->
|
<!-- Show ellipsis if current page is > 4 -->
|
||||||
@@ -28,7 +30,7 @@
|
|||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
<!-- Show pages around current page -->
|
<!-- Show pages around current page -->
|
||||||
{{ range .Paginator.Pagers }}
|
{{ range $paginator.Pagers }}
|
||||||
{{ $pageNumber := .PageNumber }}
|
{{ $pageNumber := .PageNumber }}
|
||||||
{{ if and (gt $pageNumber 1) (lt $pageNumber $totalPages) (ge $pageNumber (sub $currentPage 1)) (le $pageNumber (add $currentPage 1)) }}
|
{{ if and (gt $pageNumber 1) (lt $pageNumber $totalPages) (ge $pageNumber (sub $currentPage 1)) (le $pageNumber (add $currentPage 1)) }}
|
||||||
{{ if ne $pageNumber 1 }}
|
{{ if ne $pageNumber 1 }}
|
||||||
@@ -51,16 +53,16 @@
|
|||||||
<!-- Always show last page -->
|
<!-- Always show last page -->
|
||||||
{{ if gt $totalPages 1 }}
|
{{ if gt $totalPages 1 }}
|
||||||
<li class="page-item {{ if eq $currentPage $totalPages }}active{{ end }}">
|
<li class="page-item {{ if eq $currentPage $totalPages }}active{{ end }}">
|
||||||
<a class="page-link" href="{{ .Paginator.Last.URL }}">{{ $totalPages }}</a>
|
<a class="page-link" href="{{ $paginator.Last.URL }}">{{ $totalPages }}</a>
|
||||||
</li>
|
</li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
<!-- Next -->
|
<!-- Next -->
|
||||||
<li class="page-item {{ if not .Paginator.HasNext }}disabled{{ end }}">
|
<li class="page-item {{ if not $paginator.HasNext }}disabled{{ end }}">
|
||||||
{{ if .Paginator.HasNext }}
|
{{ if $paginator.HasNext }}
|
||||||
<a class="page-link" href="{{ .Paginator.Next.URL }}">Next »</a>
|
<a class="page-link" href="{{ $paginator.Next.URL }}">Suivant »</a>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<a class="page-link" href="#">Next »</a>
|
<a class="page-link" href="#">Suivant »</a>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
3
layouts/partials/scripts.html
Normal file
3
layouts/partials/scripts.html
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<!-- Search Scripts -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/lunr@2.3.9/lunr.min.js"></script>
|
||||||
|
<script src="/assets/js/search.js"></script>
|
||||||
32
layouts/partials/seo/favicons.html
Normal file
32
layouts/partials/seo/favicons.html
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<!-- Favicon -->
|
||||||
|
<link rel="icon" type="image/x-icon" href="/assets/images/favicon.ico">
|
||||||
|
<link rel="shortcut icon" type="image/x-icon" href="/assets/images/favicon.ico">
|
||||||
|
|
||||||
|
<!-- Apple Touch Icons -->
|
||||||
|
<link rel="apple-touch-icon" sizes="57x57" href="/assets/images/apple-touch-icon-57x57.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="60x60" href="/assets/images/apple-touch-icon-60x60.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="72x72" href="/assets/images/apple-touch-icon-72x72.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="76x76" href="/assets/images/apple-touch-icon-76x76.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="114x114" href="/assets/images/apple-touch-icon-114x114.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="120x120" href="/assets/images/apple-touch-icon-120x120.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="144x144" href="/assets/images/apple-touch-icon-144x144.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="152x152" href="/assets/images/apple-touch-icon-152x152.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/assets/images/apple-touch-icon-180x180.png">
|
||||||
|
|
||||||
|
<!-- Android Icons -->
|
||||||
|
<link rel="icon" type="image/png" sizes="192x192" href="/assets/images/android-icon-192x192.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/assets/images/favicon-32x32.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="96x96" href="/assets/images/favicon-96x96.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/assets/images/favicon-16x16.png">
|
||||||
|
|
||||||
|
<!-- Microsoft Tiles -->
|
||||||
|
<meta name="msapplication-TileColor" content="{{ .Site.Params.seo.theme_color | default "#007bff" }}">
|
||||||
|
<meta name="msapplication-TileImage" content="/assets/images/ms-icon-144x144.png">
|
||||||
|
<meta name="msapplication-config" content="/assets/images/browserconfig.xml">
|
||||||
|
|
||||||
|
<!-- PWA Manifest -->
|
||||||
|
<link rel="manifest" href="/manifest.json">
|
||||||
|
|
||||||
|
<!-- Theme Color -->
|
||||||
|
<meta name="theme-color" content="{{ .Site.Params.seo.theme_color | default "#007bff" }}">
|
||||||
|
<meta name="msapplication-TileColor" content="{{ .Site.Params.seo.theme_color | default "#007bff" }}">
|
||||||
34
layouts/partials/seo/head-performance.html
Normal file
34
layouts/partials/seo/head-performance.html
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{{- /* Performance optimization for SEO */ -}}
|
||||||
|
|
||||||
|
<!-- DNS Prefetch and Preconnect for performance -->
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link rel="dns-prefetch" href="//www.google-analytics.com">
|
||||||
|
<link rel="dns-prefetch" href="//googletagmanager.com">
|
||||||
|
<link rel="dns-prefetch" href="//www.googletagmanager.com">
|
||||||
|
<link rel="dns-prefetch" href="//fonts.googleapis.com">
|
||||||
|
<link rel="dns-prefetch" href="//fonts.gstatic.com">
|
||||||
|
|
||||||
|
<!-- Resource hints for common CDNs -->
|
||||||
|
<link rel="preconnect" href="https://cdnjs.cloudflare.com">
|
||||||
|
<link rel="preconnect" href="https://unpkg.com">
|
||||||
|
|
||||||
|
<!-- Prefetch critical resources -->
|
||||||
|
<link rel="prefetch" href="/assets/css/theme.css">
|
||||||
|
<link rel="prefetch" href="/assets/images/logo.png">
|
||||||
|
|
||||||
|
<!-- Preload critical fonts -->
|
||||||
|
<link rel="preload" href="/assets/fonts/main-font.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
|
<link rel="preload" href="/assets/fonts/icon-font.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
|
|
||||||
|
<!-- Performance meta tags -->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="HandheldFriendly" content="True">
|
||||||
|
<meta name="MobileOptimized" content="320">
|
||||||
|
|
||||||
|
<!-- Disable phone number detection -->
|
||||||
|
<meta name="format-detection" content="telephone=no">
|
||||||
|
<meta name="format-detection" content="date=no">
|
||||||
|
<meta name="format-detection" content="address=no">
|
||||||
|
<meta name="format-detection" content="email=no">
|
||||||
31
layouts/partials/seo/hreflang.html
Normal file
31
layouts/partials/seo/hreflang.html
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{{- /* hreflang implementation for multilingual SEO */ -}}
|
||||||
|
|
||||||
|
<!-- French regional variations -->
|
||||||
|
<link rel="alternate" hreflang="fr-fr" href="https://www.mistergeek.net{{ .RelPermalink }}" />
|
||||||
|
<!-- <link rel="alternate" hreflang="fr-be" href="https://www.mistergeek.net/be{{ .RelPermalink }}" />
|
||||||
|
<link rel="alternate" hreflang="fr-ca" href="https://www.mistergeek.net/ca{{ .RelPermalink }}" />
|
||||||
|
<link rel="alternate" hreflang="fr-ch" href="https://www.mistergeek.net/ch{{ .RelPermalink }}" />
|
||||||
|
<link rel="alternate" hreflang="fr-lu" href="https://www.mistergeek.net/lu{{ .RelPermalink }}" /> -->
|
||||||
|
|
||||||
|
<!-- Default fallback -->
|
||||||
|
<link rel="alternate" hreflang="x-default" href="https://www.mistergeek.net{{ .RelPermalink }}" />
|
||||||
|
|
||||||
|
<!-- English version if exists -->
|
||||||
|
{{ range .Translations }}
|
||||||
|
{{ if eq .Language.Lang "en" }}
|
||||||
|
<link rel="alternate" hreflang="en" href="{{ .Permalink }}" />
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<!-- Other translations -->
|
||||||
|
{{ range .Translations }}
|
||||||
|
{{ if ne .Language.Lang "en" }}
|
||||||
|
<link rel="alternate" hreflang="{{ .Language.Lang }}" href="{{ .Permalink }}" />
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<!-- Canonical URL -->
|
||||||
|
<link rel="canonical" href="{{ .Permalink }}">
|
||||||
|
|
||||||
|
<!-- Mobile alternate -->
|
||||||
|
<link rel="alternate" media="only screen and (max-width: 640px)" href="{{ .Permalink }}">
|
||||||
84
layouts/partials/seo/meta-dynamic.html
Normal file
84
layouts/partials/seo/meta-dynamic.html
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
{{- /* Dynamic meta tags based on content type */ -}}
|
||||||
|
|
||||||
|
{{- $description := "" -}}
|
||||||
|
{{- $keywords := "" -}}
|
||||||
|
{{- $title := .Title | default .Site.Title -}}
|
||||||
|
|
||||||
|
{{/* Dynamic description based on section */}}
|
||||||
|
{{- if eq .Section "tutorials" -}}
|
||||||
|
{{- $description = printf "Complete tutorial: %s. Learn %s in French with our step-by-step guide. %s"
|
||||||
|
.Title
|
||||||
|
(.Params.skill | default "the technology")
|
||||||
|
(.Params.summary | default "") -}}
|
||||||
|
{{- $keywords = printf "tutorial %s, french guide %s, %s, tutoriel informatique, guide technologie"
|
||||||
|
(.Params.category | default "")
|
||||||
|
(.Params.skill | default "")
|
||||||
|
(.Title | lower) -}}
|
||||||
|
|
||||||
|
{{- else if eq .Section "comparisons" -}}
|
||||||
|
{{- $description = printf "Detailed comparison: %s. %s Which to choose in 2024? Complete reviews and tests."
|
||||||
|
.Title
|
||||||
|
(.Params.summary | default "") -}}
|
||||||
|
{{- $keywords = printf "comparison %s, best %s 2024, review %s, comparatif %s"
|
||||||
|
(.Params.category | default "")
|
||||||
|
(.Params.category | default "")
|
||||||
|
(.Title | lower)
|
||||||
|
(.Params.category | default "") -}}
|
||||||
|
|
||||||
|
{{- else if eq .Section "security" -}}
|
||||||
|
{{- $description = printf "Computer security guide: %s. %s Learn to protect your data and systems effectively."
|
||||||
|
.Title
|
||||||
|
(.Params.summary | default "") -}}
|
||||||
|
{{- $keywords = printf "security %s, cybersecurity %s, protection %s, sécurité informatique"
|
||||||
|
(.Params.category | default "")
|
||||||
|
(.Title | lower)
|
||||||
|
(.Params.category | default "") -}}
|
||||||
|
|
||||||
|
{{- else if eq .Section "wordpress" -}}
|
||||||
|
{{- $description = printf "WordPress tutorial: %s. %s Complete guide to create and manage your WordPress site in French."
|
||||||
|
.Title
|
||||||
|
(.Params.summary | default "") -}}
|
||||||
|
{{- $keywords = printf "wordpress %s, tutorial wordpress %s, guide wordpress %s, créer site wordpress"
|
||||||
|
(.Params.category | default "")
|
||||||
|
(.Params.category | default "")
|
||||||
|
(.Title | lower) -}}
|
||||||
|
|
||||||
|
{{- else -}}
|
||||||
|
{{- $description = .Description | default .Summary | default .Site.Params.description | default .Site.Title -}}
|
||||||
|
{{- $keywords = delimit (.Keywords | default .Site.Params.keywords | default (slice)) ", " -}}
|
||||||
|
{{- end -}}
|
||||||
|
|
||||||
|
<!-- Dynamic meta tags -->
|
||||||
|
<meta name="description" content="{{ $description | truncate 160 }}">
|
||||||
|
<meta name="keywords" content="{{ $keywords | lower }}">
|
||||||
|
|
||||||
|
<!-- Content-type specific meta tags -->
|
||||||
|
{{- if eq .Section "tutorials" }}
|
||||||
|
<meta name="tutorial-type" content="{{ .Params.type | default "guide" }}">
|
||||||
|
<meta name="skill-level" content="{{ .Params.level | default "beginner" }}">
|
||||||
|
<meta name="estimated-time" content="{{ .Params.duration | default "30 min" }}">
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- if eq .Section "comparisons" }}
|
||||||
|
<meta name="comparison-year" content="2024">
|
||||||
|
<meta name="review-type" content="detailed">
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
<!-- Article published/modified dates -->
|
||||||
|
{{- if .IsPage }}
|
||||||
|
<meta name="article:published_time" content="{{ .Date.Format "2006-01-02T15:04:05Z07:00" }}">
|
||||||
|
<meta name="article:modified_time" content="{{ .Lastmod.Format "2006-01-02T15:04:05Z07:00" }}">
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
<!-- Author information -->
|
||||||
|
{{- $author := .Params.author | default .Site.Params.author | default .Site.Title }}
|
||||||
|
<meta name="article:author" content="{{ $author }}">
|
||||||
|
<meta name="author" content="{{ $author }}">
|
||||||
|
|
||||||
|
<!-- Category and tags -->
|
||||||
|
{{- if .Params.category }}
|
||||||
|
<meta name="article:section" content="{{ .Params.category }}">
|
||||||
|
{{- end }}
|
||||||
|
{{- range .Params.tags }}
|
||||||
|
<meta name="article:tag" content="{{ . }}">
|
||||||
|
{{- end }}
|
||||||
33
layouts/partials/seo/opengraph.html
Normal file
33
layouts/partials/seo/opengraph.html
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{{- $title := .Title | default .Site.Title -}}
|
||||||
|
{{- $description := .Description | default .Summary | default .Site.Params.description | default .Site.Title -}}
|
||||||
|
{{- $image := .Params.image | default .Site.Params.seo.default_image | default "/assets/images/og-default.jpg" -}}
|
||||||
|
{{- $image = $image | absURL -}}
|
||||||
|
{{- $url := .Permalink | default .RelPermalink | absURL -}}
|
||||||
|
{{- $siteName := .Site.Title -}}
|
||||||
|
{{- $locale := .Site.Language.Lang | default "en_US" -}}
|
||||||
|
|
||||||
|
<!-- Open Graph / Facebook -->
|
||||||
|
<meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}">
|
||||||
|
<meta property="og:site_name" content="{{ $siteName }}">
|
||||||
|
<meta property="og:title" content="{{ $title }}">
|
||||||
|
<meta property="og:description" content="{{ $description }}">
|
||||||
|
<meta property="og:url" content="{{ $url }}">
|
||||||
|
<meta property="og:locale" content="{{ $locale }}">
|
||||||
|
<meta property="og:image" content="{{ $image }}">
|
||||||
|
<meta property="og:image:alt" content="{{ $title }}">
|
||||||
|
<meta property="og:image:width" content="1200">
|
||||||
|
<meta property="og:image:height" content="630">
|
||||||
|
|
||||||
|
<!-- Article specific -->
|
||||||
|
{{ if .IsPage }}
|
||||||
|
<meta property="article:section" content="{{ .Section | default "general" }}">
|
||||||
|
<meta property="article:author" content="{{ .Params.author | default .Site.Params.author | default .Site.Title }}">
|
||||||
|
{{ range .Params.tags | default .Site.Params.tags }}
|
||||||
|
<meta property="article:tag" content="{{ . }}">
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<!-- Additional locales for multilingual sites -->
|
||||||
|
{{ range .Translations }}
|
||||||
|
<meta property="og:locale:alternate" content="{{ .Language.Lang | default "en_US" }}">
|
||||||
|
{{ end }}
|
||||||
57
layouts/partials/seo/schema-article.html
Normal file
57
layouts/partials/seo/schema-article.html
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
{{- /* Advanced Article Schema with enhanced metadata */ -}}
|
||||||
|
|
||||||
|
{{- $title := .Title | default .Site.Title -}}
|
||||||
|
{{- $description := .Description | default .Summary | default .Site.Params.description | default .Site.Title -}}
|
||||||
|
{{- $image := .Params.image | default .Site.Params.seo.default_image | default "/assets/images/logo.png" -}}
|
||||||
|
{{- $image = $image | absURL -}}
|
||||||
|
{{- $url := .Permalink | default .RelPermalink | absURL -}}
|
||||||
|
{{- $author := .Params.author | default .Site.Params.author | default .Site.Title -}}
|
||||||
|
{{- $datePublished := .Date.Format "2006-01-02T15:04:05Z07:00" -}}
|
||||||
|
{{- $dateModified := .Lastmod.Format "2006-01-02T15:04:05Z07:00" -}}
|
||||||
|
{{- $keywords := delimit (.Keywords | default .Site.Params.keywords | default (slice)) ", " -}}
|
||||||
|
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": ["TechArticle", "Article"],
|
||||||
|
"headline": "{{ $title }}",
|
||||||
|
"description": "{{ $description }}",
|
||||||
|
"image": {
|
||||||
|
"@type": "ImageObject",
|
||||||
|
"url": "{{ $image }}",
|
||||||
|
"width": 1200,
|
||||||
|
"height": 630
|
||||||
|
},
|
||||||
|
"url": "{{ $url }}",
|
||||||
|
"author": {
|
||||||
|
"@type": "Person",
|
||||||
|
"name": "{{ $author }}",
|
||||||
|
"url": "{{ .Site.BaseURL }}",
|
||||||
|
"sameAs": [
|
||||||
|
"https://twitter.com/mistergeekfrance",
|
||||||
|
"https://www.linkedin.com/in/mistergeek",
|
||||||
|
"https://www.youtube.com/@mistergeek"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"publisher": {
|
||||||
|
"@type": "Organization",
|
||||||
|
"name": "{{ .Site.Title }}",
|
||||||
|
"url": "{{ .Site.BaseURL }}",
|
||||||
|
"logo": {
|
||||||
|
"@type": "ImageObject",
|
||||||
|
"url": "{{ .Site.BaseURL }}assets/images/logo.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"datePublished": "{{ $datePublished }}",
|
||||||
|
"dateModified": "{{ $dateModified }}",
|
||||||
|
"inLanguage": "fr-FR",
|
||||||
|
"keywords": "{{ $keywords }}",
|
||||||
|
"articleSection": "{{ .Section | default "general" }}",
|
||||||
|
"wordCount": {{ .WordCount }},
|
||||||
|
"articleBody": "{{ .Plain | truncate 200 }}",
|
||||||
|
"mainEntityOfPage": {
|
||||||
|
"@type": "WebPage",
|
||||||
|
"@id": "{{ $url }}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
126
layouts/partials/seo/schema-local.html
Normal file
126
layouts/partials/seo/schema-local.html
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
{{- /* Local SEO Schema.org markup for French market */ -}}
|
||||||
|
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "Organization",
|
||||||
|
"name": "Mistergeek",
|
||||||
|
"url": "https://www.mistergeek.net",
|
||||||
|
"logo": {
|
||||||
|
"@type": "ImageObject",
|
||||||
|
"url": "https://www.mistergeek.net/assets/images/logo.png",
|
||||||
|
"width": 400,
|
||||||
|
"height": 400
|
||||||
|
},
|
||||||
|
"description": "Mistergeek - Tutoriels et guides en informatique et technologie",
|
||||||
|
"founder": {
|
||||||
|
"@type": "Person",
|
||||||
|
"name": "Mistergeek",
|
||||||
|
"url": "https://www.mistergeek.net"
|
||||||
|
},
|
||||||
|
"foundingDate": "2020",
|
||||||
|
"address": {
|
||||||
|
"@type": "PostalAddress",
|
||||||
|
"addressCountry": "FR",
|
||||||
|
"addressRegion": "Île-de-France",
|
||||||
|
"addressLocality": "Paris",
|
||||||
|
"postalCode": "75000"
|
||||||
|
},
|
||||||
|
"contactPoint": {
|
||||||
|
"@type": "ContactPoint",
|
||||||
|
"contactType": "support",
|
||||||
|
"email": "contact@mistergeek.net",
|
||||||
|
"availableLanguage": ["French"],
|
||||||
|
"areaServed": ["FR", "BE", "CA", "CH", "LU"],
|
||||||
|
"hoursAvailable": {
|
||||||
|
"@type": "OpeningHoursSpecification",
|
||||||
|
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
|
||||||
|
"opens": "09:00",
|
||||||
|
"closes": "18:00"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sameAs": [
|
||||||
|
"https://twitter.com/mistergeekfrance",
|
||||||
|
"https://www.linkedin.com/in/mistergeek",
|
||||||
|
"https://www.youtube.com/@mistergeek",
|
||||||
|
"https://github.com/mistergeek",
|
||||||
|
"https://www.facebook.com/mistergeek.fr"
|
||||||
|
],
|
||||||
|
"hasOfferCatalog": {
|
||||||
|
"@type": "OfferCatalog",
|
||||||
|
"name": "Tutoriels Informatique",
|
||||||
|
"itemListElement": [
|
||||||
|
{
|
||||||
|
"@type": "Offer",
|
||||||
|
"itemOffered": {
|
||||||
|
"@type": "Service",
|
||||||
|
"name": "Tutoriels WordPress",
|
||||||
|
"description": "Guides complets pour créer et gérer un site WordPress"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "Offer",
|
||||||
|
"itemOffered": {
|
||||||
|
"@type": "Service",
|
||||||
|
"name": "Tutoriels Sécurité",
|
||||||
|
"description": "Apprenez à sécuriser vos systèmes et données"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "Offer",
|
||||||
|
"itemOffered": {
|
||||||
|
"@type": "Service",
|
||||||
|
"name": "Comparatifs Logiciels",
|
||||||
|
"description": "Analyses détaillées des meilleurs logiciels"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"potentialAction": {
|
||||||
|
"@type": "SearchAction",
|
||||||
|
"target": {
|
||||||
|
"@type": "EntryPoint",
|
||||||
|
"urlTemplate": "https://www.mistergeek.net/search?q={search_term_string}"
|
||||||
|
},
|
||||||
|
"query-input": "required name=search_term_string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Local Business Schema for Google My Business -->
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "EducationalOrganization",
|
||||||
|
"name": "Mistergeek Academy",
|
||||||
|
"url": "https://www.mistergeek.net",
|
||||||
|
"logo": "https://www.mistergeek.net/assets/images/logo.png",
|
||||||
|
"description": "Formation et tutoriels en informatique et technologie",
|
||||||
|
"address": {
|
||||||
|
"@type": "PostalAddress",
|
||||||
|
"streetAddress": "123 Rue de l'Informatique",
|
||||||
|
"addressLocality": "Paris",
|
||||||
|
"addressRegion": "Île-de-France",
|
||||||
|
"postalCode": "75000",
|
||||||
|
"addressCountry": "FR"
|
||||||
|
},
|
||||||
|
"geo": {
|
||||||
|
"@type": "GeoCoordinates",
|
||||||
|
"latitude": 48.8566,
|
||||||
|
"longitude": 2.3522
|
||||||
|
},
|
||||||
|
"telephone": "+33-1-23-45-67-89",
|
||||||
|
"email": "contact@mistergeek.net",
|
||||||
|
"openingHoursSpecification": [
|
||||||
|
{
|
||||||
|
"@type": "OpeningHoursSpecification",
|
||||||
|
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
|
||||||
|
"opens": "09:00",
|
||||||
|
"closes": "18:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"priceRange": "€",
|
||||||
|
"currenciesAccepted": "EUR",
|
||||||
|
"paymentAccepted": "Cash, Credit Card, PayPal"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
87
layouts/partials/seo/schema-tutorial.html
Normal file
87
layouts/partials/seo/schema-tutorial.html
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
{{- /* Tutorial-specific Schema.org markup */ -}}
|
||||||
|
|
||||||
|
{{- $title := .Title | default .Site.Title -}}
|
||||||
|
{{- $description := .Description | default .Summary | default .Site.Params.description | default .Site.Title -}}
|
||||||
|
{{- $image := .Params.image | default .Site.Params.seo.default_image | default "/assets/images/logo.png" -}}
|
||||||
|
{{- $image = $image | absURL -}}
|
||||||
|
{{- $url := .Permalink | default .RelPermalink | absURL -}}
|
||||||
|
{{- $author := .Params.author | default .Site.Params.author | default .Site.Title -}}
|
||||||
|
{{- $duration := .Params.duration | default "PT30M" -}}
|
||||||
|
{{- $difficulty := .Params.level | default "beginner" -}}
|
||||||
|
{{- $category := .Params.category | default "technology" -}}
|
||||||
|
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "HowTo",
|
||||||
|
"name": "{{ $title }}",
|
||||||
|
"description": "{{ $description }}",
|
||||||
|
"image": "{{ $image }}",
|
||||||
|
"totalTime": "{{ $duration }}",
|
||||||
|
"estimatedCost": {
|
||||||
|
"@type": "MonetaryAmount",
|
||||||
|
"currency": "EUR",
|
||||||
|
"value": "0"
|
||||||
|
},
|
||||||
|
"tool": [
|
||||||
|
{
|
||||||
|
"@type": "HowToTool",
|
||||||
|
"name": "{{ .Params.primary_tool | default "Ordinateur" }}"
|
||||||
|
}
|
||||||
|
{{- if .Params.tools -}}
|
||||||
|
{{- range .Params.tools -}}
|
||||||
|
,{
|
||||||
|
"@type": "HowToTool",
|
||||||
|
"name": "{{ . }}"
|
||||||
|
}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
],
|
||||||
|
"supply": [
|
||||||
|
{
|
||||||
|
"@type": "HowToSupply",
|
||||||
|
"name": "{{ .Params.primary_supply | default "Connexion Internet" }}"
|
||||||
|
}
|
||||||
|
{{- if .Params.supplies -}}
|
||||||
|
{{- range .Params.supplies -}}
|
||||||
|
,{
|
||||||
|
"@type": "HowToSupply",
|
||||||
|
"name": "{{ . }}"
|
||||||
|
}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
],
|
||||||
|
"step": [
|
||||||
|
{{- range $index, $step := .Params.steps -}}
|
||||||
|
{{- if $index }},{{ end }}
|
||||||
|
{
|
||||||
|
"@type": "HowToStep",
|
||||||
|
"position": {{ add $index 1 }},
|
||||||
|
"name": "{{ $step.name }}",
|
||||||
|
"text": "{{ $step.description | default $step.text }}",
|
||||||
|
"url": "{{ $url }}#step-{{ add $index 1 }}",
|
||||||
|
"image": "{{ $image }}"
|
||||||
|
}
|
||||||
|
{{- end -}}
|
||||||
|
],
|
||||||
|
"educationalLevel": "{{ $difficulty }}",
|
||||||
|
"teaches": "{{ .Params.skill | default "compétences informatiques" }}",
|
||||||
|
"inLanguage": "fr-FR",
|
||||||
|
"audience": {
|
||||||
|
"@type": "EducationalAudience",
|
||||||
|
"educationalRole": "learner"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"@type": "Person",
|
||||||
|
"name": "{{ $author }}",
|
||||||
|
"url": "{{ .Site.BaseURL }}"
|
||||||
|
},
|
||||||
|
"publisher": {
|
||||||
|
"@type": "Organization",
|
||||||
|
"name": "{{ .Site.Title }}",
|
||||||
|
"url": "{{ .Site.BaseURL }}"
|
||||||
|
},
|
||||||
|
"datePublished": "{{ .Date.Format "2006-01-02T15:04:05Z07:00" }}",
|
||||||
|
"dateModified": "{{ .Lastmod.Format "2006-01-02T15:04:05Z07:00" }}"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
71
layouts/partials/seo/seo-config.html
Normal file
71
layouts/partials/seo/seo-config.html
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
{{- /* SEO Configuration Partial */ -}}
|
||||||
|
{{- /* This partial includes all SEO-related partials */ -}}
|
||||||
|
|
||||||
|
<!-- Performance optimizations -->
|
||||||
|
{{ partial "seo/head-performance.html" . }}
|
||||||
|
|
||||||
|
<!-- Core SEO Meta Tags -->
|
||||||
|
{{ partial "seo/seo-meta.html" . }}
|
||||||
|
|
||||||
|
<!-- Dynamic meta tags based on content type -->
|
||||||
|
{{ partial "seo/meta-dynamic.html" . }}
|
||||||
|
|
||||||
|
<!-- Open Graph Tags -->
|
||||||
|
{{ partial "seo/opengraph.html" . }}
|
||||||
|
|
||||||
|
<!-- Twitter Cards -->
|
||||||
|
{{ partial "seo/twitter-cards.html" . }}
|
||||||
|
|
||||||
|
<!-- Structured Data (JSON-LD) -->
|
||||||
|
{{ partial "seo/structured-data.html" . }}
|
||||||
|
|
||||||
|
<!-- Favicons and PWA Support -->
|
||||||
|
{{ partial "seo/favicons.html" . }}
|
||||||
|
|
||||||
|
<!-- hreflang for multilingual sites -->
|
||||||
|
{{ partial "seo/hreflang.html" . }}
|
||||||
|
|
||||||
|
<!-- Additional SEO Tags -->
|
||||||
|
{{- if .Site.Params.seo.google_verification }}
|
||||||
|
<meta name="google-site-verification" content="{{ .Site.Params.seo.google_verification }}">
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{- if .Site.Params.seo.bing_verification }}
|
||||||
|
<meta name="msvalidate.01" content="{{ .Site.Params.seo.bing_verification }}">
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{- if .Site.Params.seo.yandex_verification }}
|
||||||
|
<meta name="yandex-verification" content="{{ .Site.Params.seo.yandex_verification }}">
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{- if .Site.Params.seo.alexa_verification }}
|
||||||
|
<meta name="alexaVerifyID" content="{{ .Site.Params.seo.alexa_verification }}">
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<!-- Analytics and Tracking -->
|
||||||
|
{{- if .Site.Params.seo.google_analytics }}
|
||||||
|
<!-- Google Analytics 4 -->
|
||||||
|
<script async src="https://www.googletagmanager.com/gtag/js?id={{ .Site.Params.seo.google_analytics }}"></script>
|
||||||
|
<script>
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag(){dataLayer.push(arguments);}
|
||||||
|
gtag('js', new Date());
|
||||||
|
gtag('config', '{{ .Site.Params.seo.google_analytics }}');
|
||||||
|
</script>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{- if .Site.Params.seo.google_tag_manager }}
|
||||||
|
<!-- Google Tag Manager -->
|
||||||
|
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
||||||
|
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
||||||
|
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
||||||
|
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
||||||
|
})(window,document,'script','dataLayer','{{ .Site.Params.seo.google_tag_manager }}');</script>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<!-- Additional verification codes -->
|
||||||
|
{{- range $name, $code := .Site.Params.seo.verification }}
|
||||||
|
{{- if $code }}
|
||||||
|
<meta name="{{ $name }}" content="{{ $code }}">
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
40
layouts/partials/seo/seo-meta.html
Normal file
40
layouts/partials/seo/seo-meta.html
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
{{- $description := .Description | default .Summary | default .Site.Params.description | default .Site.Title -}}
|
||||||
|
{{- $keywords := delimit (.Keywords | default .Site.Params.keywords | default (slice)) ", " -}}
|
||||||
|
{{- $author := .Params.author | default .Site.Params.author | default .Site.Title -}}
|
||||||
|
{{- $robots := .Params.robots | default "index, follow" -}}
|
||||||
|
{{- $canonical := .Permalink | default .RelPermalink -}}
|
||||||
|
|
||||||
|
<!-- Primary Meta Tags -->
|
||||||
|
<meta name="description" content="{{ $description }}">
|
||||||
|
<meta name="keywords" content="{{ $keywords }}">
|
||||||
|
<meta name="author" content="{{ $author }}">
|
||||||
|
<meta name="robots" content="{{ $robots }}">
|
||||||
|
<meta name="generator" content="Hugo {{ hugo.Version }}">
|
||||||
|
|
||||||
|
<!-- Canonical URL -->
|
||||||
|
<link rel="canonical" href="{{ $canonical }}">
|
||||||
|
|
||||||
|
<!-- Theme Color -->
|
||||||
|
<meta name="theme-color" content="{{ .Site.Params.seo.theme_color | default "#007bff" }}">
|
||||||
|
<meta name="msapplication-TileColor" content="{{ .Site.Params.seo.theme_color | default "#007bff" }}">
|
||||||
|
|
||||||
|
<!-- Additional SEO -->
|
||||||
|
<meta name="rating" content="general">
|
||||||
|
<meta name="language" content="{{ .Site.Language.Lang | default "en" }}">
|
||||||
|
<meta name="revisit-after" content="7 days">
|
||||||
|
<meta name="distribution" content="global">
|
||||||
|
<meta name="web_author" content="{{ .Site.Params.author | default .Site.Title }}">
|
||||||
|
|
||||||
|
<!-- Geo Tags (if location is specified) -->
|
||||||
|
{{ with .Site.Params.seo.geo }}
|
||||||
|
<meta name="geo.region" content="{{ .region }}">
|
||||||
|
<meta name="geo.placename" content="{{ .placename }}">
|
||||||
|
<meta name="geo.position" content="{{ .latitude }};{{ .longitude }}">
|
||||||
|
<meta name="ICBM" content="{{ .latitude }}, {{ .longitude }}">
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<!-- Dublin Core -->
|
||||||
|
<meta name="DC.Title" content="{{ .Title | default .Site.Title }}">
|
||||||
|
<meta name="DC.Creator" content="{{ $author }}">
|
||||||
|
<meta name="DC.Description" content="{{ $description }}">
|
||||||
|
<meta name="DC.Language" content="{{ .Site.Language.Lang | default "en" }}">
|
||||||
133
layouts/partials/seo/structured-data.html
Normal file
133
layouts/partials/seo/structured-data.html
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
{{- $title := .Title | default .Site.Title -}}
|
||||||
|
{{- $description := .Description | default .Summary | default .Site.Params.description | default .Site.Title -}}
|
||||||
|
{{- $image := .Params.image | default .Site.Params.seo.default_image | default "/assets/images/logo.png" -}}
|
||||||
|
{{- $image = $image | absURL -}}
|
||||||
|
{{- $url := .Permalink | default .RelPermalink | absURL -}}
|
||||||
|
{{- $siteName := .Site.Title -}}
|
||||||
|
{{- $logo := .Site.Params.seo.logo | default "/assets/images/logo.png" | absURL -}}
|
||||||
|
{{- $author := .Params.author | default .Site.Params.author | default .Site.Title -}}
|
||||||
|
{{- $datePublished := .Date.Format "2006-01-02T15:04:05Z07:00" -}}
|
||||||
|
{{- $dateModified := .Lastmod.Format "2006-01-02T15:04:05Z07:00" -}}
|
||||||
|
{{- $keywords := delimit (.Keywords | default .Site.Params.keywords | default (slice)) ", " -}}
|
||||||
|
|
||||||
|
<!-- Enhanced WebSite Schema -->
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "WebSite",
|
||||||
|
"name": "{{ $siteName }}",
|
||||||
|
"description": "{{ .Site.Params.description }}",
|
||||||
|
"url": "{{ .Site.BaseURL }}",
|
||||||
|
"logo": {
|
||||||
|
"@type": "ImageObject",
|
||||||
|
"url": "{{ $logo }}",
|
||||||
|
"width": 400,
|
||||||
|
"height": 400
|
||||||
|
},
|
||||||
|
"potentialAction": {
|
||||||
|
"@type": "SearchAction",
|
||||||
|
"target": {
|
||||||
|
"@type": "EntryPoint",
|
||||||
|
"urlTemplate": "{{ .Site.BaseURL }}search?q={search_term_string}"
|
||||||
|
},
|
||||||
|
"query-input": "required name=search_term_string"
|
||||||
|
},
|
||||||
|
"inLanguage": "fr-FR",
|
||||||
|
"dateModified": "{{ now.Format "2006-01-02T15:04:05Z07:00" }}"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Enhanced Article Schema -->
|
||||||
|
{{ if .IsPage }}
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": ["Article", "TechArticle"],
|
||||||
|
"headline": "{{ $title }}",
|
||||||
|
"description": "{{ $description }}",
|
||||||
|
"image": {
|
||||||
|
"@type": "ImageObject",
|
||||||
|
"url": "{{ $image }}",
|
||||||
|
"width": 1200,
|
||||||
|
"height": 630
|
||||||
|
},
|
||||||
|
"url": "{{ $url }}",
|
||||||
|
"author": {
|
||||||
|
"@type": "Person",
|
||||||
|
"name": "{{ $author }}",
|
||||||
|
"url": "{{ .Site.BaseURL }}",
|
||||||
|
"sameAs": [
|
||||||
|
"https://twitter.com/mistergeekfrance",
|
||||||
|
"https://www.linkedin.com/in/mistergeek",
|
||||||
|
"https://www.youtube.com/@mistergeek"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"publisher": {
|
||||||
|
"@type": "Organization",
|
||||||
|
"name": "{{ $siteName }}",
|
||||||
|
"url": "{{ .Site.BaseURL }}",
|
||||||
|
"logo": {
|
||||||
|
"@type": "ImageObject",
|
||||||
|
"url": "{{ $logo }}",
|
||||||
|
"width": 400,
|
||||||
|
"height": 400
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"datePublished": "{{ $datePublished }}",
|
||||||
|
"dateModified": "{{ $dateModified }}",
|
||||||
|
"inLanguage": "fr-FR",
|
||||||
|
"keywords": "{{ $keywords }}",
|
||||||
|
"articleSection": "{{ .Section | default "general" }}",
|
||||||
|
"wordCount": {{ .WordCount }},
|
||||||
|
"mainEntityOfPage": {
|
||||||
|
"@type": "WebPage",
|
||||||
|
"@id": "{{ $url }}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<!-- Tutorial-specific Schema -->
|
||||||
|
{{ if eq .Section "tutorials" }}
|
||||||
|
{{ partial "seo/schema-tutorial.html" . }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<!-- Enhanced Article Schema for blog posts -->
|
||||||
|
{{ if or (eq .Section "blog") (eq .Section "posts") }}
|
||||||
|
{{ partial "seo/schema-article.html" . }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<!-- Local SEO Schema -->
|
||||||
|
{{ if .IsHome }}
|
||||||
|
{{ partial "seo/schema-local.html" . }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<!-- Breadcrumb Schema -->
|
||||||
|
{{ if and .IsPage .Section }}
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "BreadcrumbList",
|
||||||
|
"itemListElement": [
|
||||||
|
{
|
||||||
|
"@type": "ListItem",
|
||||||
|
"position": 1,
|
||||||
|
"name": "Accueil",
|
||||||
|
"item": "{{ .Site.BaseURL }}"
|
||||||
|
}{{ if .Section }},
|
||||||
|
{
|
||||||
|
"@type": "ListItem",
|
||||||
|
"position": 2,
|
||||||
|
"name": "{{ .Section | humanize }}",
|
||||||
|
"item": "{{ .Site.BaseURL }}{{ .Section }}/"
|
||||||
|
}{{ if .Title }},
|
||||||
|
{
|
||||||
|
"@type": "ListItem",
|
||||||
|
"position": 3,
|
||||||
|
"name": "{{ $title }}",
|
||||||
|
"item": "{{ $url }}"
|
||||||
|
}{{ end }}{{ end }}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{{ end }}
|
||||||
27
layouts/partials/seo/twitter-cards.html
Normal file
27
layouts/partials/seo/twitter-cards.html
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{{- $title := .Title | default .Site.Title -}}
|
||||||
|
{{- $description := .Description | default .Summary | default .Site.Params.description | default .Site.Title -}}
|
||||||
|
{{- $image := .Params.image | default .Site.Params.seo.default_image | default "/assets/images/twitter-default.jpg" -}}
|
||||||
|
{{- $image = $image | absURL -}}
|
||||||
|
{{- $siteName := .Site.Title -}}
|
||||||
|
{{- $twitter := .Site.Params.seo.twitter -}}
|
||||||
|
|
||||||
|
<!-- Twitter Card -->
|
||||||
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
|
<meta name="twitter:title" content="{{ $title }}">
|
||||||
|
<meta name="twitter:description" content="{{ $description }}">
|
||||||
|
<meta name="twitter:image" content="{{ $image }}">
|
||||||
|
<meta name="twitter:image:alt" content="{{ $title }}">
|
||||||
|
|
||||||
|
<!-- Twitter Site -->
|
||||||
|
{{ with $twitter.site }}
|
||||||
|
<meta name="twitter:site" content="{{ . }}">
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<!-- Twitter Creator -->
|
||||||
|
{{ with $twitter.creator }}
|
||||||
|
<meta name="twitter:creator" content="{{ . }}">
|
||||||
|
{{ else }}
|
||||||
|
{{ with .Params.author }}
|
||||||
|
<meta name="twitter:creator" content="{{ . }}">
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
42
nginx.conf
Normal file
42
nginx.conf
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name mistergeek.fr mistergeek.net agence-webside.fr;
|
||||||
|
return 301 https://www.mistergeek.net$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name www.mistergeek.net;
|
||||||
|
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html index.htm;
|
||||||
|
|
||||||
|
# Enable gzip compression
|
||||||
|
gzip on;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_min_length 1024;
|
||||||
|
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
|
||||||
|
|
||||||
|
# Cache static assets
|
||||||
|
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Security headers
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
|
||||||
|
# Main location block
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Health check endpoint
|
||||||
|
location /health {
|
||||||
|
access_log off;
|
||||||
|
return 200 "healthy\n";
|
||||||
|
add_header Content-Type text/plain;
|
||||||
|
}
|
||||||
|
}
|
||||||
510
package-lock.json
generated
510
package-lock.json
generated
@@ -10,6 +10,349 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"he": "^1.2.0",
|
"he": "^1.2.0",
|
||||||
"node-fetch": "^3.3.2"
|
"node-fetch": "^3.3.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"sass": "^1.69.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"detect-libc": "^1.0.3",
|
||||||
|
"is-glob": "^4.0.3",
|
||||||
|
"micromatch": "^4.0.5",
|
||||||
|
"node-addon-api": "^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@parcel/watcher-android-arm64": "2.5.1",
|
||||||
|
"@parcel/watcher-darwin-arm64": "2.5.1",
|
||||||
|
"@parcel/watcher-darwin-x64": "2.5.1",
|
||||||
|
"@parcel/watcher-freebsd-x64": "2.5.1",
|
||||||
|
"@parcel/watcher-linux-arm-glibc": "2.5.1",
|
||||||
|
"@parcel/watcher-linux-arm-musl": "2.5.1",
|
||||||
|
"@parcel/watcher-linux-arm64-glibc": "2.5.1",
|
||||||
|
"@parcel/watcher-linux-arm64-musl": "2.5.1",
|
||||||
|
"@parcel/watcher-linux-x64-glibc": "2.5.1",
|
||||||
|
"@parcel/watcher-linux-x64-musl": "2.5.1",
|
||||||
|
"@parcel/watcher-win32-arm64": "2.5.1",
|
||||||
|
"@parcel/watcher-win32-ia32": "2.5.1",
|
||||||
|
"@parcel/watcher-win32-x64": "2.5.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher-android-arm64": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher-darwin-arm64": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher-darwin-x64": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher-freebsd-x64": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher-linux-arm-glibc": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher-linux-arm-musl": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher-linux-arm64-glibc": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher-linux-arm64-musl": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher-linux-x64-glibc": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher-linux-x64-musl": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher-win32-arm64": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher-win32-ia32": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher-win32-x64": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/braces": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"fill-range": "^7.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/chokidar": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"readdirp": "^4.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14.16.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/data-uri-to-buffer": {
|
"node_modules/data-uri-to-buffer": {
|
||||||
@@ -21,6 +364,20 @@
|
|||||||
"node": ">= 12"
|
"node": ">= 12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/detect-libc": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"bin": {
|
||||||
|
"detect-libc": "bin/detect-libc.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fetch-blob": {
|
"node_modules/fetch-blob": {
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
|
||||||
@@ -44,6 +401,20 @@
|
|||||||
"node": "^12.20 || >= 14.13"
|
"node": "^12.20 || >= 14.13"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fill-range": {
|
||||||
|
"version": "7.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||||
|
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"to-regex-range": "^5.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/formdata-polyfill": {
|
"node_modules/formdata-polyfill": {
|
||||||
"version": "4.0.10",
|
"version": "4.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
|
||||||
@@ -65,6 +436,72 @@
|
|||||||
"he": "bin/he"
|
"he": "bin/he"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/immutable": {
|
||||||
|
"version": "5.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz",
|
||||||
|
"integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/is-extglob": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-glob": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"is-extglob": "^2.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-number": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/micromatch": {
|
||||||
|
"version": "4.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||||
|
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"braces": "^3.0.3",
|
||||||
|
"picomatch": "^2.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-addon-api": {
|
||||||
|
"version": "7.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
|
||||||
|
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/node-domexception": {
|
"node_modules/node-domexception": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
||||||
@@ -103,6 +540,79 @@
|
|||||||
"url": "https://opencollective.com/node-fetch"
|
"url": "https://opencollective.com/node-fetch"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/picomatch": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/readdirp": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14.18.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/sass": {
|
||||||
|
"version": "1.90.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/sass/-/sass-1.90.0.tgz",
|
||||||
|
"integrity": "sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"chokidar": "^4.0.0",
|
||||||
|
"immutable": "^5.0.2",
|
||||||
|
"source-map-js": ">=0.6.2 <2.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"sass": "sass.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@parcel/watcher": "^2.4.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/source-map-js": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/to-regex-range": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"is-number": "^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/web-streams-polyfill": {
|
"node_modules/web-streams-polyfill": {
|
||||||
"version": "3.3.3",
|
"version": "3.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
|
||||||
|
|||||||
@@ -6,12 +6,16 @@
|
|||||||
"fetch-data": "node scripts/fetch-wordpress.js",
|
"fetch-data": "node scripts/fetch-wordpress.js",
|
||||||
"generate-content": "node scripts/generate-content.js",
|
"generate-content": "node scripts/generate-content.js",
|
||||||
"prebuild": "npm run fetch-data && npm run generate-content",
|
"prebuild": "npm run fetch-data && npm run generate-content",
|
||||||
"build": "hugo --minify",
|
"build": "npm run build:css && hugo --minify",
|
||||||
"dev": "npm run fetch-data && npm run generate-content && hugo server -D",
|
"build:css": "sass assets/css/scss:static/assets/css",
|
||||||
|
"dev": "npm run fetch-data && npm run generate-content && npm run build:css && hugo server -D",
|
||||||
"clean": "rm -rf data/wordpress content/posts public"
|
"clean": "rm -rf data/wordpress content/posts public"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"he": "^1.2.0",
|
"he": "^1.2.0",
|
||||||
"node-fetch": "^3.3.2"
|
"node-fetch": "^3.3.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"sass": "^1.69.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
20
scripts/build.sh
Executable file
20
scripts/build.sh
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Exit immediately if a command exits with a non-zero status.
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Building CSS..."
|
||||||
|
# Assuming sass is installed and available in the environment or Docker image
|
||||||
|
# Compile theme.scss to theme.css
|
||||||
|
sass assets/css/scss/theme.scss static/assets/css/theme.css
|
||||||
|
|
||||||
|
echo "Fetching content from WordPress..."
|
||||||
|
# Assuming Node.js is installed and available in the environment or Docker image
|
||||||
|
node scripts/fetch-wordpress.js
|
||||||
|
|
||||||
|
echo "Generating production build with Hugo..."
|
||||||
|
|
||||||
|
# Assuming Hugo is installed and available in the environment or Docker image
|
||||||
|
hugo --minify --environment production --config hugo.toml
|
||||||
|
|
||||||
|
echo "Build process completed successfully!"
|
||||||
@@ -4,32 +4,32 @@ const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch
|
|||||||
|
|
||||||
const WORDPRESS_API = 'https://www.mistergeek.net/wp-json/wp/v2';
|
const WORDPRESS_API = 'https://www.mistergeek.net/wp-json/wp/v2';
|
||||||
const OUTPUT_DIR = path.join(__dirname, '..', 'data', 'wordpress');
|
const OUTPUT_DIR = path.join(__dirname, '..', 'data', 'wordpress');
|
||||||
|
const HUGO_DATA_DIR = path.join(__dirname, '..', 'data');
|
||||||
|
|
||||||
async function fetchPosts(page = 1, perPage = 100) {
|
async function fetchAll(endpoint, perPage = 100) {
|
||||||
const response = await fetch(`${WORDPRESS_API}/posts?page=${page}&per_page=${perPage}&_embed`);
|
let page = 1;
|
||||||
const posts = await response.json();
|
let items = [];
|
||||||
|
|
||||||
if (response.headers.get('x-wp-totalpages') > page) {
|
while (true) {
|
||||||
const nextPosts = await fetchPosts(page + 1, perPage);
|
const url = `${WORDPRESS_API}/${endpoint}?page=${page}&per_page=${perPage}&_embed`;
|
||||||
return [...posts, ...nextPosts];
|
const response = await fetch(url);
|
||||||
|
|
||||||
|
// If endpoint does not support paging or returns empty, break
|
||||||
|
if (!response.ok) {
|
||||||
|
// 400 often means "page out of range" for WP; stop paging
|
||||||
|
if (response.status === 400) break;
|
||||||
|
throw new Error(`Failed to fetch ${endpoint} (page ${page}): ${response.status} ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const batch = await response.json();
|
||||||
|
items = items.concat(batch);
|
||||||
|
|
||||||
|
const totalPages = parseInt(response.headers.get('x-wp-totalpages') || '1', 10);
|
||||||
|
if (page >= totalPages) break;
|
||||||
|
page++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return posts;
|
return items;
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchCategories() {
|
|
||||||
const response = await fetch(`${WORDPRESS_API}/categories?per_page=100`);
|
|
||||||
return response.json();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchTags() {
|
|
||||||
const response = await fetch(`${WORDPRESS_API}/tags?per_page=100`);
|
|
||||||
return response.json();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchAuthors() {
|
|
||||||
const response = await fetch(`${WORDPRESS_API}/users?per_page=100`);
|
|
||||||
return response.json();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function generateData() {
|
async function generateData() {
|
||||||
@@ -40,20 +40,95 @@ async function generateData() {
|
|||||||
|
|
||||||
console.log('Fetching WordPress data...');
|
console.log('Fetching WordPress data...');
|
||||||
|
|
||||||
const [posts, categories, tags, authors] = await Promise.all([
|
// Fetch all relevant endpoints concurrently (pages + posts + taxonomies + users)
|
||||||
fetchPosts(),
|
const [
|
||||||
fetchCategories(),
|
posts,
|
||||||
fetchTags(),
|
pages,
|
||||||
fetchAuthors()
|
categories,
|
||||||
|
tags,
|
||||||
|
authors
|
||||||
|
] = await Promise.all([
|
||||||
|
fetchAll('posts'),
|
||||||
|
fetchAll('pages'),
|
||||||
|
fetchAll('categories'),
|
||||||
|
fetchAll('tags'),
|
||||||
|
fetchAll('users')
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Filter pages to only include published pages
|
||||||
|
const publishedPages = pages.filter(page => page.status === 'publish');
|
||||||
|
|
||||||
|
// Create navigation data from published pages
|
||||||
|
const navigationData = publishedPages.map(page => ({
|
||||||
|
id: page.id,
|
||||||
|
title: page.title?.rendered || page.slug,
|
||||||
|
slug: page.slug,
|
||||||
|
link: page.link,
|
||||||
|
date: page.date,
|
||||||
|
modified: page.modified
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Create author-post mapping
|
||||||
|
const authorPosts = {};
|
||||||
|
posts.forEach(post => {
|
||||||
|
if (post.status === 'publish') {
|
||||||
|
const author = post._embedded?.author?.[0];
|
||||||
|
if (author) {
|
||||||
|
const authorSlug = author.slug;
|
||||||
|
if (!authorPosts[authorSlug]) {
|
||||||
|
authorPosts[authorSlug] = {
|
||||||
|
id: author.id,
|
||||||
|
name: author.name,
|
||||||
|
slug: author.slug,
|
||||||
|
description: author.description || '',
|
||||||
|
avatar: author.avatar_urls || {},
|
||||||
|
link: author.link,
|
||||||
|
posts: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
authorPosts[authorSlug].posts.push({
|
||||||
|
id: post.id,
|
||||||
|
title: post.title.rendered,
|
||||||
|
slug: post.slug,
|
||||||
|
date: post.date,
|
||||||
|
excerpt: post.excerpt.rendered,
|
||||||
|
featured_image: post._embedded?.['wp:featuredmedia']?.[0]?.source_url || '',
|
||||||
|
categories: (post._embedded?.['wp:term']?.[0] || []).map(cat => ({
|
||||||
|
id: cat.id,
|
||||||
|
name: cat.name,
|
||||||
|
slug: cat.slug
|
||||||
|
})),
|
||||||
|
tags: (post._embedded?.['wp:term']?.[1] || []).map(tag => ({
|
||||||
|
id: tag.id,
|
||||||
|
name: tag.name,
|
||||||
|
slug: tag.slug
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Save data as JSON files
|
// Save data as JSON files
|
||||||
fs.writeFileSync(path.join(OUTPUT_DIR, 'posts.json'), JSON.stringify(posts, null, 2));
|
fs.writeFileSync(path.join(OUTPUT_DIR, 'posts.json'), JSON.stringify(posts, null, 2));
|
||||||
|
fs.writeFileSync(path.join(OUTPUT_DIR, 'pages.json'), JSON.stringify(publishedPages, null, 2));
|
||||||
|
// Extract category descriptions
|
||||||
|
const categoriesWithDescriptions = categories.map(cat => ({
|
||||||
|
id: cat.id,
|
||||||
|
name: cat.name,
|
||||||
|
slug: cat.slug,
|
||||||
|
description: cat.description || '',
|
||||||
|
count: cat.count || 0
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Also save to wordpress directory
|
||||||
fs.writeFileSync(path.join(OUTPUT_DIR, 'categories.json'), JSON.stringify(categories, null, 2));
|
fs.writeFileSync(path.join(OUTPUT_DIR, 'categories.json'), JSON.stringify(categories, null, 2));
|
||||||
fs.writeFileSync(path.join(OUTPUT_DIR, 'tags.json'), JSON.stringify(tags, null, 2));
|
fs.writeFileSync(path.join(OUTPUT_DIR, 'tags.json'), JSON.stringify(tags, null, 2));
|
||||||
fs.writeFileSync(path.join(OUTPUT_DIR, 'authors.json'), JSON.stringify(authors, null, 2));
|
fs.writeFileSync(path.join(OUTPUT_DIR, 'authors.json'), JSON.stringify(authors, null, 2));
|
||||||
|
fs.writeFileSync(path.join(OUTPUT_DIR, 'author-posts.json'), JSON.stringify(authorPosts, null, 2));
|
||||||
|
fs.writeFileSync(path.join(OUTPUT_DIR, 'navigation.json'), JSON.stringify(navigationData, null, 2));
|
||||||
|
|
||||||
console.log(`✅ Fetched ${posts.length} posts, ${categories.length} categories, ${tags.length} tags, ${authors.length} authors`);
|
console.log(`✅ Fetched ${posts.length} posts, ${publishedPages.length} pages, ${categories.length} categories, ${tags.length} tags, ${authors.length} authors`);
|
||||||
|
console.log(`✅ Generated navigation data with ${navigationData.length} items`);
|
||||||
}
|
}
|
||||||
|
|
||||||
generateData().catch(console.error);
|
generateData().catch(console.error);
|
||||||
@@ -4,16 +4,23 @@ const he = require('he');
|
|||||||
|
|
||||||
const DATA_DIR = path.join(__dirname, '..', 'data', 'wordpress');
|
const DATA_DIR = path.join(__dirname, '..', 'data', 'wordpress');
|
||||||
const CONTENT_DIR = path.join(__dirname, '..', 'content');
|
const CONTENT_DIR = path.join(__dirname, '..', 'content');
|
||||||
|
const PAGES_DIR = path.join(CONTENT_DIR, 'pages');
|
||||||
|
|
||||||
function generateContent() {
|
function generateContent() {
|
||||||
const posts = JSON.parse(fs.readFileSync(path.join(DATA_DIR, 'posts.json'), 'utf8'));
|
const posts = JSON.parse(fs.readFileSync(path.join(DATA_DIR, 'posts.json'), 'utf8'));
|
||||||
|
const pages = JSON.parse(fs.readFileSync(path.join(DATA_DIR, 'pages.json'), 'utf8'));
|
||||||
|
|
||||||
// Ensure content directory exists
|
// Ensure content directories exist
|
||||||
if (!fs.existsSync(CONTENT_DIR)) {
|
if (!fs.existsSync(CONTENT_DIR)) {
|
||||||
fs.mkdirSync(CONTENT_DIR, { recursive: true });
|
fs.mkdirSync(CONTENT_DIR, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
posts.forEach(post => {
|
if (!fs.existsSync(PAGES_DIR)) {
|
||||||
|
fs.mkdirSync(PAGES_DIR, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process posts - only include published posts
|
||||||
|
posts.filter(post => post.status === 'publish').forEach(post => {
|
||||||
const slug = post.slug;
|
const slug = post.slug;
|
||||||
const date = new Date(post.date);
|
const date = new Date(post.date);
|
||||||
const year = date.getFullYear();
|
const year = date.getFullYear();
|
||||||
@@ -33,7 +40,7 @@ function generateContent() {
|
|||||||
const frontmatter = {
|
const frontmatter = {
|
||||||
title: he.decode(post.title.rendered),
|
title: he.decode(post.title.rendered),
|
||||||
date: post.date,
|
date: post.date,
|
||||||
draft: post.status !== 'publish',
|
draft: false,
|
||||||
slug: slug,
|
slug: slug,
|
||||||
wordpress_id: post.id,
|
wordpress_id: post.id,
|
||||||
excerpt: he.decode(post.excerpt.rendered.replace(/<[^>]*>/g, '')),
|
excerpt: he.decode(post.excerpt.rendered.replace(/<[^>]*>/g, '')),
|
||||||
@@ -47,21 +54,27 @@ function generateContent() {
|
|||||||
// Decode HTML entities in the content and clean up HTML tags
|
// Decode HTML entities in the content and clean up HTML tags
|
||||||
let contentHtml = he.decode(post.content.rendered);
|
let contentHtml = he.decode(post.content.rendered);
|
||||||
|
|
||||||
// Convert absolute URLs in a href to relative URLs
|
// Convert absolute URLs in a href to relative URLs (only for wp.mistergeek.net)
|
||||||
contentHtml = contentHtml.replace(/<a\s+[^>]*href="([^"]+)"[^>]*>/g, (match, href) => {
|
contentHtml = contentHtml.replace(/<a\s+[^>]*href="([^"]+)"[^>]*>/g, (match, href) => {
|
||||||
// Check if the href is an absolute URL (starts with http:// or https://)
|
// Check if the href is an absolute URL containing wp.mistergeek.net
|
||||||
if (href.startsWith('http://') || href.startsWith('https://')) {
|
if (href.startsWith('http://') || href.startsWith('https://')) {
|
||||||
// Extract the path part of the URL
|
try {
|
||||||
const url = new URL(href);
|
const url = new URL(href);
|
||||||
// Return the modified a tag with relative URL
|
if (url.hostname === 'wp.mistergeek.net' || url.hostname === 'www.wp.mistergeek.net') {
|
||||||
return match.replace(href, url.pathname);
|
// Only convert wp.mistergeek.net URLs to relative paths
|
||||||
|
return match.replace(href, url.pathname);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// If URL parsing fails, return the original href
|
||||||
|
console.warn('Failed to parse URL:', href, error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return match;
|
return match;
|
||||||
});
|
});
|
||||||
|
|
||||||
contentHtml = contentHtml
|
contentHtml = contentHtml
|
||||||
.replace(/<p>\s*<\/p>/g, '') // Remove empty paragraphs
|
.replace(/<p>\s*<\/p>/g, '') // Remove empty paragraphs
|
||||||
.replace(/<\/p>\s*<p>/g, '\n\n') // Replace paragraph breaks with newlines
|
.replace(/<\/p>\s*<p>/g, '\n\n'); // Replace paragraph breaks with newlines
|
||||||
|
|
||||||
const content = `---
|
const content = `---
|
||||||
${Object.entries(frontmatter)
|
${Object.entries(frontmatter)
|
||||||
@@ -73,7 +86,156 @@ ${contentHtml.trim()}`;
|
|||||||
fs.writeFileSync(path.join(contentDir, 'index.md'), content);
|
fs.writeFileSync(path.join(contentDir, 'index.md'), content);
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`✅ Generated ${posts.length} content files`);
|
// Process pages - only include published pages
|
||||||
|
pages.filter(page => page.status === 'publish').forEach(page => {
|
||||||
|
const slug = page.slug;
|
||||||
|
|
||||||
|
const contentDir = path.join(PAGES_DIR, slug);
|
||||||
|
|
||||||
|
if (!fs.existsSync(contentDir)) {
|
||||||
|
fs.mkdirSync(contentDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
const frontmatter = {
|
||||||
|
title: he.decode(page.title.rendered),
|
||||||
|
slug: slug,
|
||||||
|
type: "pages",
|
||||||
|
layout: "single",
|
||||||
|
wordpress_id: page.id,
|
||||||
|
date: page.date,
|
||||||
|
modified: page.modified,
|
||||||
|
draft: false,
|
||||||
|
aliases: [`/${slug}/`]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Decode HTML entities in the content and clean up HTML tags
|
||||||
|
let contentHtml = he.decode(page.content.rendered);
|
||||||
|
|
||||||
|
// Convert absolute URLs in a href to relative URLs (only for wp.mistergeek.net)
|
||||||
|
contentHtml = contentHtml.replace(/<a\s+[^>]*href="([^"]+)"[^>]*>/g, (match, href) => {
|
||||||
|
// Check if the href is an absolute URL containing wp.mistergeek.net
|
||||||
|
if (href.startsWith('http://') || href.startsWith('https://')) {
|
||||||
|
try {
|
||||||
|
const url = new URL(href);
|
||||||
|
if (url.hostname === 'wp.mistergeek.net' || url.hostname === 'www.wp.mistergeek.net') {
|
||||||
|
// Only convert wp.mistergeek.net URLs to relative paths
|
||||||
|
return match.replace(href, url.pathname);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// If URL parsing fails, return the original href
|
||||||
|
console.warn('Failed to parse URL:', href, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return match;
|
||||||
|
});
|
||||||
|
|
||||||
|
contentHtml = contentHtml
|
||||||
|
.replace(/<p>\s*<\/p>/g, '') // Remove empty paragraphs
|
||||||
|
.replace(/<\/p>\s*<p>/g, '\n\n'); // Replace paragraph breaks with newlines
|
||||||
|
|
||||||
|
const content = `---
|
||||||
|
${Object.entries(frontmatter)
|
||||||
|
.map(([key, value]) => `${key}: ${JSON.stringify(value)}`)
|
||||||
|
.join('\n')}
|
||||||
|
---
|
||||||
|
${contentHtml.trim()}`;
|
||||||
|
|
||||||
|
fs.writeFileSync(path.join(contentDir, 'index.md'), content);
|
||||||
|
});
|
||||||
|
|
||||||
|
const publishedPosts = posts.filter(post => post.status === 'publish');
|
||||||
|
const publishedPages = pages.filter(page => page.status === 'publish');
|
||||||
|
|
||||||
|
// Generate author directories and index pages
|
||||||
|
generateAuthorDirectories(publishedPosts);
|
||||||
|
|
||||||
|
console.log(`✅ Generated ${publishedPosts.length} content files`);
|
||||||
|
console.log(`✅ Generated ${publishedPages.length} page files`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateAuthorDirectories(posts) {
|
||||||
|
const AUTHORS_DIR = path.join(CONTENT_DIR, 'author');
|
||||||
|
|
||||||
|
// Ensure authors directory exists
|
||||||
|
if (!fs.existsSync(AUTHORS_DIR)) {
|
||||||
|
fs.mkdirSync(AUTHORS_DIR, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group posts by author using proper author data
|
||||||
|
const postsByAuthor = {};
|
||||||
|
|
||||||
|
posts.forEach(post => {
|
||||||
|
const author = post._embedded?.author?.[0];
|
||||||
|
if (author) {
|
||||||
|
const authorSlug = author.slug;
|
||||||
|
const authorName = author.name;
|
||||||
|
|
||||||
|
if (!postsByAuthor[authorSlug]) {
|
||||||
|
postsByAuthor[authorSlug] = {
|
||||||
|
id: author.id,
|
||||||
|
name: authorName,
|
||||||
|
slug: authorSlug,
|
||||||
|
description: author.description || '',
|
||||||
|
avatar: author.avatar_urls || {},
|
||||||
|
link: author.link,
|
||||||
|
posts: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
postsByAuthor[authorSlug].posts.push(post);
|
||||||
|
} else {
|
||||||
|
// Handle unknown author
|
||||||
|
const unknownSlug = 'unknown';
|
||||||
|
const unknownName = 'Unknown';
|
||||||
|
|
||||||
|
if (!postsByAuthor[unknownSlug]) {
|
||||||
|
postsByAuthor[unknownSlug] = {
|
||||||
|
id: 0,
|
||||||
|
name: unknownName,
|
||||||
|
slug: unknownSlug,
|
||||||
|
description: '',
|
||||||
|
avatar: {},
|
||||||
|
link: '',
|
||||||
|
posts: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
postsByAuthor[unknownSlug].posts.push(post);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create author directories and index pages
|
||||||
|
Object.values(postsByAuthor).forEach(author => {
|
||||||
|
const authorDir = path.join(AUTHORS_DIR, author.slug);
|
||||||
|
|
||||||
|
if (!fs.existsSync(authorDir)) {
|
||||||
|
fs.mkdirSync(authorDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate author index page with proper metadata
|
||||||
|
const frontmatter = {
|
||||||
|
title: `Articles de ${author.name}`,
|
||||||
|
type: 'authors',
|
||||||
|
layout: 'list',
|
||||||
|
author: author.name,
|
||||||
|
author_slug: author.slug,
|
||||||
|
description: author.description,
|
||||||
|
avatar: author.avatar,
|
||||||
|
link: author.link,
|
||||||
|
post_count: author.posts.length
|
||||||
|
};
|
||||||
|
|
||||||
|
const content = `---
|
||||||
|
${Object.entries(frontmatter)
|
||||||
|
.map(([key, value]) => `${key}: ${JSON.stringify(value)}`)
|
||||||
|
.join('\n')}
|
||||||
|
---
|
||||||
|
`;
|
||||||
|
|
||||||
|
fs.writeFileSync(path.join(authorDir, '_index.md'), content);
|
||||||
|
|
||||||
|
console.log(`✅ Generated author directory: ${author.name} (${author.posts.length} posts)`);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
generateContent();
|
generateContent();
|
||||||
File diff suppressed because it is too large
Load Diff
1
static/assets/css/theme.css.map
Normal file
1
static/assets/css/theme.css.map
Normal file
File diff suppressed because one or more lines are too long
39
test-table.html
Normal file
39
test-table.html
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Table Test</title>
|
||||||
|
<link rel="stylesheet" href="assets/css/scss/theme.scss">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<figure class="wp-block-table">
|
||||||
|
<table class="has-fixed-layout">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>TECHNOLOGIE</th>
|
||||||
|
<th>DÉBIT MAXIMUM THÉORIQUE (Mégabit/s)</th>
|
||||||
|
<th>REMARQUE(S)</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><strong>FTTH (Fibre jusqu’à l’abonné)</strong></td>
|
||||||
|
<td>Jusqu’à 10 000 Mégabit/s</td>
|
||||||
|
<td>Débit symétrique, très haute performance.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>FTTLa (Fibre jusqu’au dernier amplificateur)</strong></td>
|
||||||
|
<td>Jusqu’à 1000 Mégabit/s</td>
|
||||||
|
<td>Débit variable selon la distance au point de raccordement.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>Boucle Locale Radio</strong></td>
|
||||||
|
<td>Jusqu’à 100 Mégabit/s</td>
|
||||||
|
<td>Dépend de la qualité du signal et de l’environnement.</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</figure>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
128
yarn.lock
128
yarn.lock
@@ -2,11 +2,64 @@
|
|||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@parcel/watcher-linux-x64-glibc@2.5.1":
|
||||||
|
version "2.5.1"
|
||||||
|
resolved "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz"
|
||||||
|
integrity sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==
|
||||||
|
|
||||||
|
"@parcel/watcher-linux-x64-musl@2.5.1":
|
||||||
|
version "2.5.1"
|
||||||
|
resolved "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz"
|
||||||
|
integrity sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==
|
||||||
|
|
||||||
|
"@parcel/watcher@^2.4.1":
|
||||||
|
version "2.5.1"
|
||||||
|
resolved "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz"
|
||||||
|
integrity sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==
|
||||||
|
dependencies:
|
||||||
|
detect-libc "^1.0.3"
|
||||||
|
is-glob "^4.0.3"
|
||||||
|
micromatch "^4.0.5"
|
||||||
|
node-addon-api "^7.0.0"
|
||||||
|
optionalDependencies:
|
||||||
|
"@parcel/watcher-android-arm64" "2.5.1"
|
||||||
|
"@parcel/watcher-darwin-arm64" "2.5.1"
|
||||||
|
"@parcel/watcher-darwin-x64" "2.5.1"
|
||||||
|
"@parcel/watcher-freebsd-x64" "2.5.1"
|
||||||
|
"@parcel/watcher-linux-arm-glibc" "2.5.1"
|
||||||
|
"@parcel/watcher-linux-arm-musl" "2.5.1"
|
||||||
|
"@parcel/watcher-linux-arm64-glibc" "2.5.1"
|
||||||
|
"@parcel/watcher-linux-arm64-musl" "2.5.1"
|
||||||
|
"@parcel/watcher-linux-x64-glibc" "2.5.1"
|
||||||
|
"@parcel/watcher-linux-x64-musl" "2.5.1"
|
||||||
|
"@parcel/watcher-win32-arm64" "2.5.1"
|
||||||
|
"@parcel/watcher-win32-ia32" "2.5.1"
|
||||||
|
"@parcel/watcher-win32-x64" "2.5.1"
|
||||||
|
|
||||||
|
braces@^3.0.3:
|
||||||
|
version "3.0.3"
|
||||||
|
resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz"
|
||||||
|
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
|
||||||
|
dependencies:
|
||||||
|
fill-range "^7.1.1"
|
||||||
|
|
||||||
|
chokidar@^4.0.0:
|
||||||
|
version "4.0.3"
|
||||||
|
resolved "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz"
|
||||||
|
integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==
|
||||||
|
dependencies:
|
||||||
|
readdirp "^4.0.1"
|
||||||
|
|
||||||
data-uri-to-buffer@^4.0.0:
|
data-uri-to-buffer@^4.0.0:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz"
|
resolved "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz"
|
||||||
integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==
|
integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==
|
||||||
|
|
||||||
|
detect-libc@^1.0.3:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz"
|
||||||
|
integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==
|
||||||
|
|
||||||
fetch-blob@^3.1.2, fetch-blob@^3.1.4:
|
fetch-blob@^3.1.2, fetch-blob@^3.1.4:
|
||||||
version "3.2.0"
|
version "3.2.0"
|
||||||
resolved "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz"
|
resolved "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz"
|
||||||
@@ -15,6 +68,13 @@ fetch-blob@^3.1.2, fetch-blob@^3.1.4:
|
|||||||
node-domexception "^1.0.0"
|
node-domexception "^1.0.0"
|
||||||
web-streams-polyfill "^3.0.3"
|
web-streams-polyfill "^3.0.3"
|
||||||
|
|
||||||
|
fill-range@^7.1.1:
|
||||||
|
version "7.1.1"
|
||||||
|
resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz"
|
||||||
|
integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
|
||||||
|
dependencies:
|
||||||
|
to-regex-range "^5.0.1"
|
||||||
|
|
||||||
formdata-polyfill@^4.0.10:
|
formdata-polyfill@^4.0.10:
|
||||||
version "4.0.10"
|
version "4.0.10"
|
||||||
resolved "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz"
|
resolved "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz"
|
||||||
@@ -27,6 +87,41 @@ he@^1.2.0:
|
|||||||
resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz"
|
resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz"
|
||||||
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
|
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
|
||||||
|
|
||||||
|
immutable@^5.0.2:
|
||||||
|
version "5.1.3"
|
||||||
|
resolved "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz"
|
||||||
|
integrity sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==
|
||||||
|
|
||||||
|
is-extglob@^2.1.1:
|
||||||
|
version "2.1.1"
|
||||||
|
resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz"
|
||||||
|
integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
|
||||||
|
|
||||||
|
is-glob@^4.0.3:
|
||||||
|
version "4.0.3"
|
||||||
|
resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz"
|
||||||
|
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
|
||||||
|
dependencies:
|
||||||
|
is-extglob "^2.1.1"
|
||||||
|
|
||||||
|
is-number@^7.0.0:
|
||||||
|
version "7.0.0"
|
||||||
|
resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz"
|
||||||
|
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
|
||||||
|
|
||||||
|
micromatch@^4.0.5:
|
||||||
|
version "4.0.8"
|
||||||
|
resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz"
|
||||||
|
integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
|
||||||
|
dependencies:
|
||||||
|
braces "^3.0.3"
|
||||||
|
picomatch "^2.3.1"
|
||||||
|
|
||||||
|
node-addon-api@^7.0.0:
|
||||||
|
version "7.1.1"
|
||||||
|
resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz"
|
||||||
|
integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==
|
||||||
|
|
||||||
node-domexception@^1.0.0:
|
node-domexception@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz"
|
||||||
@@ -41,6 +136,39 @@ node-fetch@^3.3.2:
|
|||||||
fetch-blob "^3.1.4"
|
fetch-blob "^3.1.4"
|
||||||
formdata-polyfill "^4.0.10"
|
formdata-polyfill "^4.0.10"
|
||||||
|
|
||||||
|
picomatch@^2.3.1:
|
||||||
|
version "2.3.1"
|
||||||
|
resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz"
|
||||||
|
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||||
|
|
||||||
|
readdirp@^4.0.1:
|
||||||
|
version "4.1.2"
|
||||||
|
resolved "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz"
|
||||||
|
integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==
|
||||||
|
|
||||||
|
sass@^1.69.5:
|
||||||
|
version "1.90.0"
|
||||||
|
resolved "https://registry.npmjs.org/sass/-/sass-1.90.0.tgz"
|
||||||
|
integrity sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==
|
||||||
|
dependencies:
|
||||||
|
chokidar "^4.0.0"
|
||||||
|
immutable "^5.0.2"
|
||||||
|
source-map-js ">=0.6.2 <2.0.0"
|
||||||
|
optionalDependencies:
|
||||||
|
"@parcel/watcher" "^2.4.1"
|
||||||
|
|
||||||
|
"source-map-js@>=0.6.2 <2.0.0":
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz"
|
||||||
|
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
|
||||||
|
|
||||||
|
to-regex-range@^5.0.1:
|
||||||
|
version "5.0.1"
|
||||||
|
resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz"
|
||||||
|
integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
|
||||||
|
dependencies:
|
||||||
|
is-number "^7.0.0"
|
||||||
|
|
||||||
web-streams-polyfill@^3.0.3:
|
web-streams-polyfill@^3.0.3:
|
||||||
version "3.3.3"
|
version "3.3.3"
|
||||||
resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz"
|
resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz"
|
||||||
|
|||||||
Reference in New Issue
Block a user