├── content ├── _index.md ├── posts │ ├── _index.md │ ├── fiveth-post.md │ ├── fourth-post.md │ ├── second-post.md │ ├── sixth-post.md │ ├── third-post.md │ └── first-post.md └── pages │ ├── another-page.md │ ├── cv.md │ └── about.md ├── screenshot.png ├── templates ├── partials │ ├── head │ │ ├── css.html │ │ ├── js.html │ │ ├── theme-colors.html │ │ └── google-analytics.html │ ├── navigation.html │ ├── svg-icons.html │ ├── head.html │ ├── footer.html │ └── giscus.html ├── robots.txt ├── shortcodes │ ├── github_avatar.html │ ├── projects.md │ ├── contact_form.html │ └── cv.md ├── tags │ ├── single.html │ └── list.html ├── 404.html ├── base.html ├── index.html ├── tags.html ├── page.html └── macros.html ├── static ├── favicon-32x32.png ├── images │ └── blacktocat.png ├── fonts │ ├── JetBrainsMono-Bold.woff2 │ ├── custom-font-awesome.woff2 │ ├── sourcecodepro-latin.woff │ ├── JetBrainsMono-Italic.woff2 │ ├── JetBrainsMono-Regular.woff2 │ ├── sourcecodepro-cyrillic.woff │ ├── sourcecodepro-latin-ext.woff │ └── sourcecodepro-cyrillic-ext.woff └── js │ ├── monitor.js │ └── form-submission-handler.js ├── data ├── cv │ ├── education.yml │ ├── skills.yml │ ├── projects.yml │ ├── experience.yml │ └── summary.yml └── socials.yml ├── sass ├── _default_colors.scss ├── _primary-font.scss ├── _svg-icons.scss └── main.scss ├── theme.toml ├── LICENSE ├── .gitignore ├── .github └── workflows │ └── zola.yml ├── config.toml └── README.md /content/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | paginate_by = 5 3 | sort_by = "date" 4 | +++ 5 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/en9inerd/zola-hacker/HEAD/screenshot.png -------------------------------------------------------------------------------- /templates/partials/head/css.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /content/posts/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | render = false 3 | transparent = true 4 | sort_by = "date" 5 | +++ 6 | -------------------------------------------------------------------------------- /static/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/en9inerd/zola-hacker/HEAD/static/favicon-32x32.png -------------------------------------------------------------------------------- /static/images/blacktocat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/en9inerd/zola-hacker/HEAD/static/images/blacktocat.png -------------------------------------------------------------------------------- /static/fonts/JetBrainsMono-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/en9inerd/zola-hacker/HEAD/static/fonts/JetBrainsMono-Bold.woff2 -------------------------------------------------------------------------------- /static/fonts/custom-font-awesome.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/en9inerd/zola-hacker/HEAD/static/fonts/custom-font-awesome.woff2 -------------------------------------------------------------------------------- /static/fonts/sourcecodepro-latin.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/en9inerd/zola-hacker/HEAD/static/fonts/sourcecodepro-latin.woff -------------------------------------------------------------------------------- /static/fonts/JetBrainsMono-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/en9inerd/zola-hacker/HEAD/static/fonts/JetBrainsMono-Italic.woff2 -------------------------------------------------------------------------------- /static/fonts/JetBrainsMono-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/en9inerd/zola-hacker/HEAD/static/fonts/JetBrainsMono-Regular.woff2 -------------------------------------------------------------------------------- /static/fonts/sourcecodepro-cyrillic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/en9inerd/zola-hacker/HEAD/static/fonts/sourcecodepro-cyrillic.woff -------------------------------------------------------------------------------- /static/fonts/sourcecodepro-latin-ext.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/en9inerd/zola-hacker/HEAD/static/fonts/sourcecodepro-latin-ext.woff -------------------------------------------------------------------------------- /data/cv/education.yml: -------------------------------------------------------------------------------- 1 | - school: University of California, Berkeley 2 | degree: Bachelor of Science 3 | date: 2016-05-01 4 | GPA: 3.7 5 | -------------------------------------------------------------------------------- /static/fonts/sourcecodepro-cyrillic-ext.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/en9inerd/zola-hacker/HEAD/static/fonts/sourcecodepro-cyrillic-ext.woff -------------------------------------------------------------------------------- /templates/partials/head/js.html: -------------------------------------------------------------------------------- 1 | {%- if config.extra.monitor -%} 2 | 3 | {%- endif %} 4 | -------------------------------------------------------------------------------- /content/pages/another-page.md: -------------------------------------------------------------------------------- 1 | +++ 2 | path = "another-page" 3 | 4 | [extra] 5 | no_page_info = true 6 | +++ 7 | 8 | ## Welcome to another page 9 | 10 | _yay_ 11 | 12 | [back](../) 13 | -------------------------------------------------------------------------------- /data/cv/skills.yml: -------------------------------------------------------------------------------- 1 | - type: Programming Languages 2 | tools: [JavaScript/TypeScript, Python, C/C++, Bash] 3 | 4 | - type: Frameworks 5 | tools: [Angular, NestJS, LoopBack, Express.js, FastAPI, Django] 6 | -------------------------------------------------------------------------------- /content/pages/cv.md: -------------------------------------------------------------------------------- 1 | +++ 2 | path = "cv" 3 | title = "Curriculum Vitae" 4 | aliases = ["resume"] 5 | 6 | [extra] 7 | no_page_info = true 8 | 9 | [extra.seo] 10 | type = "Person" 11 | +++ 12 | 13 | {{ cv() }} 14 | -------------------------------------------------------------------------------- /templates/partials/head/theme-colors.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /templates/robots.txt: -------------------------------------------------------------------------------- 1 | {%- set disallows = [ 2 | "/404.html", 3 | "/files/*" 4 | ] -%} 5 | User-agent: * 6 | {%- for disallowed in disallows %} 7 | Disallow: {{ disallowed }} 8 | {%- endfor %} 9 | Allow: / 10 | Sitemap: {{ get_url(path="sitemap.xml") }} 11 | -------------------------------------------------------------------------------- /data/cv/projects.yml: -------------------------------------------------------------------------------- 1 | - name: "Project 1" 2 | desc: "Description of project 1" 3 | technologies: ["Python", "Django", "JavaScript", "React"] 4 | link: 5 | text: "Project 1" 6 | url: "https://www.example.com" 7 | start_date: 2021-01-01 8 | end_date: 2023-11-01 -------------------------------------------------------------------------------- /data/cv/experience.yml: -------------------------------------------------------------------------------- 1 | - employer: General Dynamics Information Technology 2 | title: Software Engineer 3 | location: Arlington, VA 4 | start_date: 2017-01-01 5 | end_date: 6 | responsibilities: 7 | - Developed and maintained a web application for the Department of Defense 8 | - Worked with a team of developers to implement new features 9 | - Wrote unit tests to ensure code quality -------------------------------------------------------------------------------- /templates/partials/navigation.html: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /templates/partials/head/google-analytics.html: -------------------------------------------------------------------------------- 1 | {%- if config.extra.google_analytics -%} 2 | 3 | 9 | {%- endif %} 10 | -------------------------------------------------------------------------------- /sass/_default_colors.scss: -------------------------------------------------------------------------------- 1 | $apple-blossom: #ac4142; 2 | $amber: #ffbf00; 3 | $alto: #d0d0d0; 4 | $bouquet: #dbbda3; 5 | $chelsea-cucumber: #90a959; 6 | $cod-grey: #151515; 7 | $conifer: #b5e853; 8 | $dove-grey: #666; 9 | $gallery: #eaeaea; 10 | $grey: #505050; 11 | $light-grey: #999; 12 | $gulf-stream: #75b5aa; 13 | $hippie-blue: #6a9fb5; 14 | $potters-clay: #8f5536; 15 | $rajah: #f4bf75; 16 | $raw-sienna: #d28445; 17 | $silver-chalice: #aaa; 18 | -------------------------------------------------------------------------------- /templates/partials/svg-icons.html: -------------------------------------------------------------------------------- 1 | {%- set socials = load_data(path="data/socials.yml") -%} 2 | {%- for link in config.extra.social_links -%} 3 | {%- if link.user_id -%} 4 | {%- set social = socials | filter(attribute="platform", value=link.platform) | first %} 5 | 6 | {%- endif -%} 7 | {%- endfor -%} -------------------------------------------------------------------------------- /templates/shortcodes/github_avatar.html: -------------------------------------------------------------------------------- 1 | {%- set size = size | default(value="400") %} 2 | {%- set url = "https://avatars.githubusercontent.com/" ~ config.extra.github.username ~ "?v=4" %} 3 | {%- set srcset = url ~ "&s=200 1x, " ~ url ~ "&s=400 2x, " ~ url ~ "&s=600 3x, " ~ url ~ "&s=800 4x" %} 4 | {{ config.extra.github.username }} -------------------------------------------------------------------------------- /theme.toml: -------------------------------------------------------------------------------- 1 | name = "zola-hacker" 2 | description = "Hacker is a theme for Zola" 3 | license = "MIT" 4 | homepage = "https://github.com/en9inerd/zola-hacker" 5 | min_version = "0.19.1" 6 | demo = "https://zola-hacker.enginerd.io/" 7 | 8 | [author] 9 | name = "Vladimir Loskutov" 10 | homepage = "https://github.com/en9inerd" 11 | 12 | [original] 13 | author = "Github Pages" 14 | homepage = "https://pages-themes.github.io/hacker/" 15 | repo = "https://github.com/pages-themes/hacker" 16 | 17 | [extra] 18 | -------------------------------------------------------------------------------- /templates/shortcodes/projects.md: -------------------------------------------------------------------------------- 1 | {%- set token = get_env(name="GITHUB_TOKEN", default="") %} 2 | {%- for repo_name in page.extra.repo_names %} 3 | {%- set url = "https://api.github.com/repos/" ~ config.extra.github.username ~ "/" ~ repo_name %} 4 | {%- if token %} 5 | {%- set repository = load_data(url=url, format="json", headers=["Authorization=Bearer " ~ token]) %} 6 | {%- else %} 7 | {%- set repository = load_data(url=url, format="json") %} 8 | {%- endif %} 9 | {%- if repository %} 10 | * [{{ repository.name }}]({{ repository.html_url }})
{{ repository.description }}

11 | {%- endif %} 12 | {%- endfor %} 13 | -------------------------------------------------------------------------------- /templates/partials/head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {% if config.generate_feed %}{% endif %} 6 | {% include "partials/head/css.html" -%} 7 | {% include "partials/head/theme-colors.html" -%} 8 | {% include "partials/head/js.html" -%} 9 | {% include "partials/head/google-analytics.html" -%} 10 | -------------------------------------------------------------------------------- /sass/_primary-font.scss: -------------------------------------------------------------------------------- 1 | $font-path: "/fonts"; 2 | 3 | @font-face { 4 | font-family: "JetBrains Mono"; 5 | font-style: normal; 6 | font-weight: 400; 7 | font-display: swap; 8 | src: url("#{$font-path}/JetBrainsMono-Regular.woff2") format("woff2"); 9 | } 10 | 11 | @font-face { 12 | font-family: "JetBrains Mono"; 13 | font-style: italic; 14 | font-weight: 400; 15 | font-display: swap; 16 | src: url("#{$font-path}/JetBrainsMono-Italic.woff2") format("woff2"); 17 | } 18 | 19 | @font-face { 20 | font-family: "JetBrains Mono"; 21 | font-style: normal; 22 | font-weight: 700; 23 | font-display: swap; 24 | src: url("#{$font-path}/JetBrainsMono-Bold.woff2") format("woff2"); 25 | } 26 | -------------------------------------------------------------------------------- /templates/partials/footer.html: -------------------------------------------------------------------------------- 1 | {%- include "partials/svg-icons.html" -%} 2 | {%- if page.extra.pgp_key_enabled and config.extra.pgp_key %} 3 |

4 | PGP: 5 | {{ config.extra.pgp_key.fingerprint }} 6 | 7 |

8 | {%- endif -%} 9 | {%- set section = get_section(path="posts/_index.md") -%} 10 | {%- set earliest_post = section.pages | last -%} 11 | {%- set earliest_date = earliest_post.date | date(format="%Y") -%} 12 | {% set current_date = now() | date(format="%Y") %} 13 | © {{ earliest_date }}{% if earliest_date != current_date %} - {{ current_date }}{% endif %} {{ 14 | config.author }} 15 | -------------------------------------------------------------------------------- /templates/tags/single.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {%- block seo %} 4 | {{- super() }} 5 | {%- set title = term.name ~ " tag" %} 6 | {%- set description = "List of pages tagged with " ~ term.name %} 7 | 8 | {%- if config.title %} 9 | {%- set title_addition = title_separator ~ config.title %} 10 | {%- else %} 11 | {%- set title_addition = "" %} 12 | {%- endif %} 13 | 14 | {{- macros::seo(title=title, title_addition=title_addition, description=description, is_page=true) }} 15 | {%- endblock seo %} 16 | 17 | {% block content %} 18 |
19 |

[ {{ term.name }} ]

20 | {% for page in term.pages %} 21 | {{ macros::page_in_list(page=page) }} 22 | {%- endfor %} 23 |
24 | {% endblock %} 25 | -------------------------------------------------------------------------------- /data/cv/summary.yml: -------------------------------------------------------------------------------- 1 | - I am a data scientist with a background in physics and a passion for solving complex problems. I have experience in developing and implementing machine learning algorithms to extract insights from large datasets. I am proficient in Python and have experience working with AWS-based data warehouse infrastructure. I am looking for opportunities to apply my skills in a dynamic and challenging environment. 2 | - I am a full-stack developer with experience in building web applications using Python, Django, JavaScript, and React. I have worked on projects ranging from e-commerce platforms to data visualization tools. I am passionate about creating user-friendly interfaces and optimizing performance. I am looking for opportunities to work on innovative projects and collaborate with talented teams. 3 | -------------------------------------------------------------------------------- /templates/tags/list.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {%- block seo %} 4 | {{- super() }} 5 | {%- set title = "Tags" %} 6 | {%- set description = "List of tags" %} 7 | 8 | {%- if config.title %} 9 | {%- set title_addition = title_separator ~ config.title %} 10 | {%- else %} 11 | {%- set title_addition = "" %} 12 | {%- endif %} 13 | 14 | {{- macros::seo(title=title, title_addition=title_addition, description=description, is_page=true) }} 15 | {%- endblock seo %} 16 | 17 | {%- block content %} 18 |

Tags

19 |
20 | 29 |
30 | {%- endblock %} 31 | -------------------------------------------------------------------------------- /content/pages/about.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "About" 3 | path = "about" 4 | 5 | [extra] 6 | no_page_info = true 7 | pgp_key_enabled = true 8 | 9 | [extra.seo] 10 | type = "Person" 11 | +++ 12 | 13 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam nec purus auctor, cursus turpis ac, ultricies nunc. Ut auctor, odio id tincidunt tincidunt, nunc metus tincidunt justo, ac ultricies nunc mi nec sapien. Nullam auctor, libero nec ultricies lacinia, mi purus vehicula mi, nec iaculis nunc elit nec felis. Nullam nec purus auctor, cursus turpis ac, ultricies nunc. Ut auctor, odio id tincidunt tincidunt, nunc metus tincidunt justo, ac ultricies nunc mi nec sapien. Nullam auctor, libero nec ultricies lacinia, mi purus vehicula mi, nec iaculis nunc elit nec felis. 14 | 15 | [Currículum Vitae](@/pages/cv.md) 16 |

17 | 18 | ## Contact Me 19 | 20 | {{ contact_form() }} 21 | -------------------------------------------------------------------------------- /templates/404.html: -------------------------------------------------------------------------------- 1 | {%- extends "base.html" %} 2 | 3 | {%- block seo -%} 4 | {{- super() }} 5 | {%- set title = "Not Found :(" %} 6 | 7 | {%- if config.title %} 8 | {%- set title_addition = title_separator ~ config.title %} 9 | {%- else %} 10 | {%- set title_addition = "" %} 11 | {%- endif %} 12 | 13 | {{- macros::seo(title=title, title_addition=title_addition, description=config.description, is_404=true) }} 14 | {%- endblock seo %} 15 | 16 | {%- block content %} 17 |
18 |
19 |

Not Found :(

20 |
21 |
22 |

23 | Sorry, but the page you were trying to view does not exist. 24 |

25 |

26 | It looks like this was the result of either: 27 |

28 | 32 |
33 |
34 | {%- endblock %} 35 | -------------------------------------------------------------------------------- /templates/partials/giscus.html: -------------------------------------------------------------------------------- 1 |
2 | 18 | -------------------------------------------------------------------------------- /templates/shortcodes/contact_form.html: -------------------------------------------------------------------------------- 1 | {%- if config.extra.contact_form_script_id %} 2 |
3 |
4 | 5 | 6 | 7 | 8 |
9 |
10 |
11 |

12 | Thanks 13 | for contacting me! 14 | I will get back to you soon! 15 |

16 |
17 |
18 | 19 | {%- endif -%} 20 | -------------------------------------------------------------------------------- /content/posts/fiveth-post.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Fiveth Post" 3 | date = 2017-01-06T00:00:00+00:00 4 | 5 | [taxonomies] 6 | tags = ["Features", "Markdown", "Second"] 7 | +++ 8 | 9 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. 10 | -------------------------------------------------------------------------------- /content/posts/fourth-post.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Fourth Post" 3 | date = 2017-01-07T00:00:00+00:00 4 | 5 | [taxonomies] 6 | tags = ["Features", "Markdown", "Second"] 7 | +++ 8 | 9 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. 10 | -------------------------------------------------------------------------------- /content/posts/second-post.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Second Post" 3 | date = 2017-01-09T00:00:00+00:00 4 | 5 | [taxonomies] 6 | tags = ["Features", "Markdown", "Second"] 7 | +++ 8 | 9 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. 10 | -------------------------------------------------------------------------------- /content/posts/sixth-post.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Sixth Post" 3 | date = 2017-01-05T00:00:00+00:00 4 | 5 | [taxonomies] 6 | tags = ["Features", "Markdown", "Second"] 7 | +++ 8 | 9 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. 10 | -------------------------------------------------------------------------------- /content/posts/third-post.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Third Post" 3 | date = 2017-01-08T00:00:00+00:00 4 | 5 | [taxonomies] 6 | tags = ["Features", "Markdown", "Second"] 7 | +++ 8 | 9 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. 10 | -------------------------------------------------------------------------------- /static/js/monitor.js: -------------------------------------------------------------------------------- 1 | // Specify the IFTTT event name and Webhooks key before enabling this script through "monitor" property in _config.yml 2 | (async function () { 3 | const EVENT_NAME = ''; 4 | const WEBHOOKS_KEY = ''; 5 | try { 6 | const { ip } = await fetch('https://api.ipify.org?format=json').then(res => res.json()); 7 | const { href: url } = window.location; 8 | const screenDimensions = `${window.screen.width} x ${window.screen.height}`; 9 | const { referrer } = document; 10 | const { userAgent } = navigator; 11 | const strToSend = encodeURIComponent([ 12 | `IP: ${ip}`, 13 | `URL: ${url}`, 14 | `Screen Dimensions: ${screenDimensions}`, 15 | `Referrer: ${referrer}`, 16 | `User Agent: ${userAgent}` 17 | ].join('
')); 18 | await fetch(`https://maker.ifttt.com/trigger/${EVENT_NAME}/with/key/${WEBHOOKS_KEY}?value1=${strToSend}`, { 19 | method: 'GET' 20 | }); 21 | } catch (error) { 22 | console.error(error); 23 | } 24 | })(); 25 | -------------------------------------------------------------------------------- /data/socials.yml: -------------------------------------------------------------------------------- 1 | - platform: email 2 | url: 'mailto:' 3 | icon: mail 4 | 5 | - platform: facebook 6 | url: https://www.facebook.com/ 7 | icon: facebook 8 | 9 | - platform: github 10 | url: https://github.com/ 11 | icon: github-circled 12 | 13 | - platform: instagram 14 | url: https://instagram.com/ 15 | icon: instagram 16 | 17 | - platform: linkedin 18 | url: https://www.linkedin.com/in/ 19 | icon: linkedin 20 | 21 | - platform: pinterest 22 | url: https://www.pinterest.com/ 23 | icon: pinterest 24 | 25 | - platform: keyoxide 26 | url: https://keyoxide.org/ 27 | icon: key 28 | 29 | - platform: feed 30 | url: /atom.xml 31 | icon: rss 32 | 33 | - platform: twitter 34 | url: https://www.twitter.com/ 35 | icon: twitter 36 | 37 | - platform: stackoverflow 38 | url: https://stackoverflow.com/ 39 | icon: stackoverflow 40 | 41 | - platform: youtube 42 | url: https://youtube.com/ 43 | icon: youtube 44 | 45 | - platform: telegram 46 | url: https://t.me/ 47 | icon: telegram 48 | -------------------------------------------------------------------------------- /templates/base.html: -------------------------------------------------------------------------------- 1 | {% import "macros.html" as macros %} 2 | 3 | {%- set lang = config.extra.language_code | default(value="en-US") -%} 4 | 5 | 6 | 7 | {% include "partials/head.html" -%} 8 | {%- block seo -%} 9 | {%- if config.extra.title_separator -%} 10 | {%- set title_separator = " " ~ config.extra.title_separator ~ " " -%} 11 | {%- else -%} 12 | {%- set title_separator = " | " -%} 13 | {%- endif -%} 14 | {%- endblock seo %} 15 | 16 | 17 | 18 |
19 |
20 | 21 |

{{ config.title }}

22 |
23 |

{{ config.description }}

24 | {% include "partials/navigation.html" -%} 25 |
26 |
27 | 28 |
29 |
30 | {%- block content %} 31 | {%- endblock %} 32 |
33 |
34 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Vladimir Loskutov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | {%- extends "base.html" %} 2 | 3 | {%- block seo -%} 4 | {{- super() }} 5 | 6 | {%- if config.title %} 7 | {%- set title = config.title %} 8 | {%- else %} 9 | {%- set title = "" %} 10 | {%- endif %} 11 | 12 | {%- if config.description and title %} 13 | {%- set title_addition = title_separator ~ config.description %} 14 | {%- elif config.description %} 15 | {%- set title_addition = config.description %} 16 | {%- else %} 17 | {%- set title_addition = "" %} 18 | {%- endif %} 19 | 20 | {{- macros::seo(title=title, title_addition=title_addition, description=config.description, is_home=true) }} 21 | {%- endblock seo %} 22 | 23 | {%- block content %} 24 |
25 | {%- for page in paginator.pages %} 26 | {{ macros::page_in_list(page=page) }} 27 | {%- endfor %} 28 |
29 | {%- if paginator.total_pages > 1 %} 30 | 41 | {%- endif -%} 42 | {%- endblock %} 43 | -------------------------------------------------------------------------------- /templates/tags.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {%- block seo %} 4 | {{- super() }} 5 | {%- set title = page.title | default(value="Tags") %} 6 | {%- set description = page.description | default(value="List of tags") %} 7 | 8 | {%- if config.title %} 9 | {%- set title_addition = title_separator ~ config.title %} 10 | {%- else %} 11 | {%- set title_addition = "" %} 12 | {%- endif %} 13 | 14 | {{- macros::seo(title=title, title_addition=title_addition, description=description, is_page=true) }} 15 | {%- endblock seo %} 16 | 17 | {%- block content %} 18 | {%- set tags = get_taxonomy(kind="tags") %} 19 | {%- set sorted_tags = tags.items | sort(attribute="name") %} 20 |
21 |

{{ page.title }}

22 |
23 |
24 | [ 25 | {%- for tag in sorted_tags %} 26 | {{ tag.name }}{% if not loop.last %},{% endif %} 27 | {%- endfor %} ] 28 | 29 |
30 |
31 |
32 | {%- for tag in sorted_tags %} 33 |

{{ tag.name }}

34 | 44 | {%- endfor %} 45 |
46 | {%- endblock %} 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig 2 | # Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,hugo 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,macos,hugo 4 | 5 | ### Hugo ### 6 | # Generated files by hugo 7 | /public/ 8 | /resources/_gen/ 9 | /assets/jsconfig.json 10 | hugo_stats.json 11 | 12 | # Executable may be added to repository 13 | hugo.exe 14 | hugo.darwin 15 | hugo.linux 16 | 17 | # Temporary lock file while building 18 | /.hugo_build.lock 19 | 20 | ### macOS ### 21 | # General 22 | .DS_Store 23 | .AppleDouble 24 | .LSOverride 25 | 26 | # Icon must end with two \r 27 | Icon 28 | 29 | # Thumbnails 30 | ._* 31 | 32 | # Files that might appear in the root of a volume 33 | .DocumentRevisions-V100 34 | .fseventsd 35 | .Spotlight-V100 36 | .TemporaryItems 37 | .Trashes 38 | .VolumeIcon.icns 39 | .com.apple.timemachine.donotpresent 40 | 41 | # Directories potentially created on remote AFP share 42 | .AppleDB 43 | .AppleDesktop 44 | Network Trash Folder 45 | Temporary Items 46 | .apdisk 47 | 48 | ### macOS Patch ### 49 | # iCloud generated files 50 | *.icloud 51 | 52 | ### VisualStudioCode ### 53 | .vscode/* 54 | !.vscode/settings.json 55 | !.vscode/tasks.json 56 | !.vscode/launch.json 57 | !.vscode/extensions.json 58 | !.vscode/*.code-snippets 59 | 60 | # Local History for Visual Studio Code 61 | .history/ 62 | 63 | # Built Visual Studio Code Extensions 64 | *.vsix 65 | 66 | ### VisualStudioCode Patch ### 67 | # Ignore all local history of files 68 | .history 69 | .ionide 70 | 71 | # End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,hugo 72 | 73 | # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) 74 | 75 | -------------------------------------------------------------------------------- /.github/workflows/zola.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | 6 | # Sample workflow for building and deploying a Zola site to GitHub Pages 7 | name: Deploy Zola site to GitHub Pages 8 | 9 | on: 10 | # Runs on pushes targeting the default branch 11 | push: 12 | branches: ["master"] 13 | 14 | # Allows you to run this workflow manually from the Actions tab 15 | workflow_dispatch: 16 | 17 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 18 | permissions: 19 | contents: read 20 | pages: write 21 | id-token: write 22 | 23 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 24 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 25 | concurrency: 26 | group: "pages" 27 | cancel-in-progress: false 28 | 29 | jobs: 30 | # Build job 31 | build: 32 | environment: github-pages 33 | runs-on: ubuntu-latest 34 | steps: 35 | - name: Checkout 36 | uses: actions/checkout@v4 37 | - name: Build Zola site 38 | uses: shalzz/zola-deploy-action@v0.19.1 39 | env: 40 | BUILD_ONLY: true 41 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 42 | - name: Upload artifact 43 | # uploads an artifact from the './public' directory 44 | uses: actions/upload-pages-artifact@v3 45 | with: 46 | path: ./public 47 | 48 | # Deployment job 49 | deploy: 50 | environment: 51 | name: github-pages 52 | url: ${{ steps.deployment.outputs.page_url }} 53 | runs-on: ubuntu-latest 54 | needs: build 55 | steps: 56 | - name: Deploy to GitHub Pages 57 | id: deployment 58 | uses: actions/deploy-pages@v4 59 | -------------------------------------------------------------------------------- /templates/page.html: -------------------------------------------------------------------------------- 1 | {%- extends "base.html" %} 2 | 3 | {%- block seo -%} 4 | {{- super() }} 5 | 6 | {%- set title_addition = "" %} 7 | {%- if page.title and config.title %} 8 | {%- set title = page.title %} 9 | {%- set title_addition = title_separator ~ config.title %} 10 | {%- elif page.title %} 11 | {%- set title = page.title %} 12 | {%- else %} 13 | {%- set title = config.title %} 14 | {%- endif %} 15 | 16 | {%- if page.relative_path is starting_with("posts/") %} 17 | {%- set description = page.content | spaceless | striptags | truncate(length=200) %} 18 | {%- set type = "article" %} 19 | {%- else %} 20 | {%- if page.description %} 21 | {%- set description = page.description %} 22 | {%- else %} 23 | {%- set description = config.description %} 24 | {%- endif %} 25 | {%- set type = "website" %} 26 | {%- endif %} 27 | 28 | {{- macros::seo(title=title, title_addition=title_addition, description=description, type=type, is_page=true) }} 29 | {% endblock seo %} 30 | 31 | {%- block content %} 32 |
33 |
34 |

{{ page.title }}

35 | {%- if config.extra.edit_page %} 36 |
37 | {{ macros::get_edit_url(page=page) }} 38 |
39 | {%- endif %} 40 |
41 | {%- if not page.extra.no_page_info %} 42 |
43 | {%- if page.taxonomies.tags %} 44 | Tags = [ 45 | {%- for tag in page.taxonomies.tags %} 46 | {%- if config.extra.simplified_tags %} 47 | {%- set term = get_taxonomy_term(kind="tags", term=tag) %} 48 | {%- set tag_url = get_url(path="@/pages/tags.md") ~ "#" ~ term.slug %} 49 | {%- else %} 50 | {%- set tag_url = get_taxonomy_url(kind="tags", name=tag) %} 51 | {%- endif %} 52 | {{ tag }} 53 | {%- if not loop.last %}, 54 | {%- endif %} 55 | {%- endfor %} ] 56 | {% endif %} 57 | 60 |
61 | {%- endif %} 62 |
63 | {{ page.content | safe }} 64 |
65 | {%- if page.relative_path is starting_with("posts/") and not page.extra.no_comments and config.extra.giscus %} 66 | {%- include "partials/giscus.html" -%} 67 | {%- endif %} 68 |
69 | {% endblock %} 70 | -------------------------------------------------------------------------------- /sass/_svg-icons.scss: -------------------------------------------------------------------------------- 1 | $fa-font-path: "/fonts"; 2 | 3 | @font-face { 4 | font-family: 'custom-font-awesome'; 5 | src: url('#{$fa-font-path}/custom-font-awesome.woff2?16233344') format('woff2'); 6 | font-weight: normal; 7 | font-style: normal; 8 | } 9 | 10 | [class^="icon-"]:before, [class*=" icon-"]:before { 11 | font-family: "custom-font-awesome"; 12 | font-style: normal; 13 | font-weight: normal; 14 | 15 | display: inline-block; 16 | text-decoration: inherit; 17 | width: 1em; 18 | margin-right: .2em; 19 | text-align: center; 20 | /* opacity: .8; */ 21 | 22 | /* For safety - reset parent styles, that can break glyph codes*/ 23 | font-variant: normal; 24 | text-transform: none; 25 | 26 | /* fix buttons height, for twitter bootstrap */ 27 | line-height: 1em; 28 | 29 | /* Animation center compensation - margins should be symmetric */ 30 | /* remove if not needed */ 31 | margin-left: .2em; 32 | 33 | /* you can be more comfortable with increased icons size */ 34 | /* font-size: 120%; */ 35 | 36 | /* Font smoothing. That was taken from TWBS */ 37 | -webkit-font-smoothing: antialiased; 38 | -moz-osx-font-smoothing: grayscale; 39 | 40 | /* Uncomment for 3D effect */ 41 | /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ 42 | } 43 | 44 | .svg-icon { 45 | font-size: 35px; 46 | display: inline-block; 47 | } 48 | 49 | .icon-key:before { content: '\e800'; } /* '' */ 50 | .icon-mail:before { content: '\e801'; } /* '' */ 51 | .icon-twitter:before { content: '\f099'; } /* '' */ 52 | .icon-facebook:before { content: '\f09a'; } /* '' */ 53 | .icon-github-circled:before { content: '\f09b'; } /* '' */ 54 | .icon-rss:before { content: '\f09e'; } /* '' */ 55 | .icon-pinterest-circled:before { content: '\f0d2'; } /* '' */ 56 | .icon-pinterest-squared:before { content: '\f0d3'; } /* '' */ 57 | .icon-mail-alt:before { content: '\f0e0'; } /* '' */ 58 | .icon-linkedin:before { content: '\f0e1'; } /* '' */ 59 | .icon-github:before { content: '\f113'; } /* '' */ 60 | .icon-rss-squared:before { content: '\f143'; } /* '' */ 61 | .icon-youtube-squared:before { content: '\f166'; } /* '' */ 62 | .icon-youtube:before { content: '\f167'; } /* '' */ 63 | .icon-youtube-play:before { content: '\f16a'; } /* '' */ 64 | .icon-stackoverflow:before { content: '\f16c'; } /* '' */ 65 | .icon-instagram:before { content: '\f16d'; } /* '' */ 66 | .icon-mail-squared:before { content: '\f199'; } /* '' */ 67 | .icon-facebook-official:before { content: '\f230'; } /* '' */ 68 | .icon-pinterest:before { content: '\f231'; } /* '' */ 69 | .icon-telegram:before { content: '\f2c6'; } /* '' */ 70 | .icon-github-squared:before { content: '\f300'; } /* '' */ 71 | .icon-twitter-squared:before { content: '\f304'; } /* '' */ 72 | .icon-facebook-squared:before { content: '\f308'; } /* '' */ 73 | .icon-linkedin-squared:before { content: '\f30c'; } /* '' */ -------------------------------------------------------------------------------- /templates/shortcodes/cv.md: -------------------------------------------------------------------------------- 1 | {%- set cv_education = load_data(path="data/cv/education.yml") -%} 2 | {%- set cv_summary = load_data(path="data/cv/summary.yml") -%} 3 | {%- set cv_experience = load_data(path="data/cv/experience.yml") | sort(attribute="start_date") | reverse -%} 4 | {%- set cv_projects = load_data(path="data/cv/projects.yml") | sort(attribute="start_date") | reverse -%} 5 | {%- set cv_skills = load_data(path="data/cv/skills.yml") -%} 6 |
7 | {%- if config.extra.cv_file %} 8 |

9 | Download as .pdf 10 |

11 | {%- endif %} 12 | 13 | ## Summary 14 | 15 | {% for punct in cv_summary %} 16 | - {{ punct }}{% endfor %} 17 | 18 | ## Skills Summary 19 | 20 | | Category | Skills | 21 | |-------------:|:---------------------| 22 | {% for skill in cv_skills %}| **{{ skill.type }}** | {% for tool in skill.tools %}{{ tool }}{% if not loop.last %}, {% endif %}{% endfor %} | 23 | {% endfor %} 24 | 25 | ## Experience 26 | 27 |
28 | {% set previous_employer="" %} 29 | {% for exp in cv_experience %} 30 | 31 | {% if exp.employer != previous_employer %} 32 | ### {{ exp.employer }} · {{exp.location}} 33 | {% endif %} 34 | 35 | #### {{ exp.title }} 36 | 37 |
38 | {% for duty in exp.responsibilities %} 39 | - {{ duty }}{% endfor %} 40 | {% set_global previous_employer=exp.employer %} 41 | {% endfor %} 42 |
43 | 44 | ## Personal Projects 45 | 46 |
47 | {% for project in cv_projects %} 48 | 49 | ### {{ project.name }}{% if project.link %} · [{{ project.link.text }}]({{ project.link.url }}){% endif %} 50 | 51 | #### Used: {{ project.technologies | join(sep=", ") }} 52 | 53 |
54 | 55 | {{ project.desc }} 56 | 57 | {% endfor %} 58 |
59 | 60 | ## Education 61 | 62 |
63 | {% for degree in cv_education %} 64 | 65 | ### {{ degree.school }} 66 | 67 | #### {{ degree.degree }} 68 | 69 |
70 | {% endfor %} 71 |
72 |
73 | -------------------------------------------------------------------------------- /content/posts/first-post.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Title" 3 | date = 2017-01-10T00:00:00+00:00 4 | 5 | [taxonomies] 6 | tags = ["Features", "Markdown"] 7 | +++ 8 | 9 | Text can be **bold**, _italic_, ~~strikethrough~~ or `keyword`. 10 | 11 | [Link to another page](@/pages/another-page.md). 12 | 13 | There should be whitespace between paragraphs. 14 | 15 | 16 | There should be whitespace between paragraphs. We recommend including a README, or a file with information about your project. 17 | 18 | # Header 1 19 | 20 | This is a normal paragraph following a header. GitHub is a code hosting platform for version control and collaboration. It lets you and others work together on projects from anywhere. 21 | 22 | ## Header 2 23 | 24 | > This is a blockquote following a header. 25 | > 26 | > When something is important enough, you do it even if the odds are not in your favor. 27 | 28 | ### Header 3 29 | 30 | ```js 31 | // Javascript code with syntax highlighting. 32 | var fun = function lang(l) { 33 | dateformat.i18n = require('./lang/' + l) 34 | return true; 35 | } 36 | ``` 37 | 38 | ```ruby 39 | # Ruby code with syntax highlighting 40 | GitHubPages::Dependencies.gems.each do |gem, version| 41 | s.add_dependency(gem, "= #{version}") 42 | end 43 | ``` 44 | 45 | #### Header 4 46 | 47 | * This is an unordered list following a header. 48 | * This is an unordered list following a header. 49 | * This is an unordered list following a header. 50 | 51 | ##### Header 5 52 | 53 | 1. This is an ordered list following a header. 54 | 2. This is an ordered list following a header. 55 | 3. This is an ordered list following a header. 56 | 57 | ###### Header 6 58 | 59 | | head1 | head two | three | 60 | |:------------:|:------------------|------:| 61 | | ok | good swedish fish | nice | 62 | | out of stock | good and plenty | nice | 63 | | ok | good `oreos` | hmm | 64 | | ok | good `zoute` drop | yumm | 65 | 66 | ### There's a horizontal rule below this. 67 | 68 | * * * 69 | 70 | ### Here is an unordered list: 71 | 72 | * Item foo 73 | * Item bar 74 | * Item baz 75 | * Item zip 76 | 77 | ### And an ordered list: 78 | 79 | 1. Item one 80 | 1. Item two 81 | 1. Item three 82 | 1. Item four 83 | 84 | ### And a nested list: 85 | 86 | - level 1 item 87 | - level 2 item 88 | - level 2 item 89 | - level 3 item 90 | - level 3 item 91 | - level 1 item 92 | - level 2 item 93 | - level 2 item 94 | - level 2 item 95 | - level 1 item 96 | - level 2 item 97 | - level 2 item 98 | - level 1 item 99 | 100 | ### Small image 101 | 102 | ![Octocat](https://github.githubassets.com/images/icons/emoji/octocat.png) 103 | 104 | ### Large image 105 | 106 | ![Branching](https://guides.github.com/activities/hello-world/branching.png) 107 | 108 | 109 | ### Definition lists can be used with HTML syntax. 110 | 111 |
112 |
Name
113 |
Godzilla
114 |
Born
115 |
1952
116 |
Birthplace
117 |
Japan
118 |
Color
119 |
Green
120 |
121 | 122 | ``` 123 | Long, single-line code blocks should not wrap. They should horizontally scroll if they are too long. This line should be long enough to demonstrate this. 124 | ``` 125 | 126 | ``` 127 | The final element. 128 | ``` 129 | 130 | ```scss, linenos 131 | pre mark { 132 | display: block; 133 | color: currentcolor; 134 | } 135 | 136 | pre table td:nth-of-type(1) { 137 | color: #6b6b6b; 138 | font-style: italic; 139 | } 140 | ``` 141 | -------------------------------------------------------------------------------- /static/js/form-submission-handler.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | // get all data in form and return object 3 | function getFormData(form) { 4 | const elements = form.elements; 5 | let itsatrap; 6 | 7 | const fields = Object.keys(elements).filter((k) => { 8 | if (elements[k].name === "itsatrap") { 9 | itsatrap = elements[k].value; 10 | return false; 11 | } 12 | return true; 13 | }).map((k) => { 14 | if (elements[k].name !== undefined) { 15 | return elements[k].name; 16 | // special case for Edge's html collection 17 | } else if (elements[k].length > 0) { 18 | return elements[k].item(0).name; 19 | } 20 | }).filter((item, pos, self) => { 21 | return self.indexOf(item) == pos && item; 22 | }); 23 | 24 | const formData = {}; 25 | fields.forEach((name) => { 26 | const element = elements[name]; 27 | 28 | // singular form elements just have one value 29 | formData[name] = element.value; 30 | 31 | // when our element has multiple items, get their values 32 | if (element.length) { 33 | const data = []; 34 | for (let i = 0; i < element.length; i++) { 35 | const item = element.item(i); 36 | if (item.checked || item.selected) { 37 | data.push(item.value); 38 | } 39 | } 40 | formData[name] = data.join(', '); 41 | } 42 | }); 43 | 44 | // add form-specific values into the data 45 | formData.formDataNameOrder = JSON.stringify(fields); 46 | formData.formGoogleSheetName = form.dataset.sheet || "responses"; // default sheet name 47 | formData.formGoogleSendEmail 48 | = form.dataset.email || ""; // no email by default 49 | 50 | return { data: formData, itsatrap }; 51 | } 52 | 53 | function handleFormSubmit(event) { 54 | event.preventDefault(); // we are submitting via fetch below 55 | const form = event.target; 56 | const formData = getFormData(form); 57 | const data = formData.data; 58 | 59 | // If a itsatrap field is filled, assume it was done so by a spam bot. 60 | if (formData.itsatrap || data.submit) { 61 | return false; 62 | } 63 | 64 | disableAllButtons(form); 65 | const url = `https://script.google.com/macros/s/${form.dataset.scriptId}/exec`; 66 | 67 | fetch(url, { 68 | method: 'POST', 69 | headers: { 70 | 'Content-Type': 'application/x-www-form-urlencoded', 71 | }, 72 | body: new URLSearchParams(data).toString(), 73 | }) 74 | .then((response) => { 75 | if (response.ok) { 76 | form.reset(); 77 | const formElements = form.querySelector(".form-elements"); 78 | if (formElements) { 79 | formElements.style.display = "none"; // hide form 80 | } 81 | const thankYouMessage = form.querySelector(".thankyou-message"); 82 | if (thankYouMessage) { 83 | thankYouMessage.style.display = "block"; 84 | } 85 | } 86 | }) 87 | .catch((error) => { 88 | console.error('Error submitting the form:', error); 89 | }); 90 | } 91 | 92 | function loaded() { 93 | // bind to the submit event of our form 94 | const forms = document.querySelectorAll("form.contact-form"); 95 | forms.forEach((form) => { 96 | form.addEventListener("submit", handleFormSubmit, false); 97 | }); 98 | }; 99 | document.addEventListener("DOMContentLoaded", loaded, false); 100 | 101 | function disableAllButtons(form) { 102 | const buttons = form.querySelectorAll("button"); 103 | buttons.forEach((button) => { 104 | button.disabled = true; 105 | }); 106 | } 107 | })(); 108 | -------------------------------------------------------------------------------- /config.toml: -------------------------------------------------------------------------------- 1 | # The URL the site will be built for 2 | base_url = "https://zola-hacker.enginerd.io/" 3 | 4 | # The title of the site 5 | title = "Hacker theme" 6 | 7 | # The description of the site 8 | description = "Hacker is a theme for Zola" 9 | 10 | author = "John Doe" 11 | 12 | # Whether to automatically compile all Sass files in the sass directory 13 | compile_sass = true 14 | 15 | # Whether to minify the HTML when building the site 16 | minify_html = false 17 | 18 | # Whether to generate an Atom feed 19 | generate_feeds = true 20 | 21 | # Taxonomies 22 | taxonomies = [{ name = "tags" }] 23 | 24 | [markdown] 25 | # Whether to do syntax highlighting 26 | # Theme can be customised by setting the `highlight_theme` variable to a theme supported by Zola 27 | highlight_code = true 28 | highlight_theme = "charcoal" 29 | 30 | # Whether to render emojis 31 | render_emoji = true 32 | 33 | # Whether footnotes are rendered in the GitHub-style (at the bottom, with back references) or plain (in the place, where they are defined) 34 | bottom_footnotes = true 35 | 36 | [extra] 37 | title_separator = "|" # set as |, -, _, etc 38 | 39 | # The path to the logo of the site 40 | logo = "images/logo.png" 41 | 42 | # Google Analytics tracking ID 43 | google_analytics = "example-tracking-id" 44 | 45 | # Contact form script id that points to google macros (e.g. https://script.google.com/macros/s//exec) 46 | # More info about contact form based on google macros can be found here: https://github.com/en9inerd/learn-to-send-email-via-google-script-html-no-server 47 | contact_form_script_id = "" 48 | 49 | # Visitor notifier based on IFTTT + Webhooks. Sample here - /static/js/monitor.js 50 | monitor = false 51 | 52 | # Menu items 53 | menu = [{ url = "/about", name = "About" }, { url = "/tags", name = "Tags" }] 54 | 55 | language_code = "en-US" 56 | 57 | # Enable OpenGraph metadata 58 | opengraph = true 59 | 60 | timezone = "America/New_York" 61 | 62 | # Date and time format for displaying post dates. 63 | # If left empty, the default format will be used: "%B %e, %Y at %H:%M %Z". 64 | # Example of a custom format: "%Y-%m-%d %H:%M:%S %z" (2024-02-04 15:30:00 +0000) 65 | # timeformat = "" 66 | 67 | # The path to cv file if you use cv shortcode 68 | # cv_file = "/files/Curriculum_Vitae.pdf" 69 | 70 | # provide link to edit page on github 71 | edit_page = true 72 | 73 | # Use theme specific tags page based on templates/tags.html 74 | simplified_tags = false 75 | 76 | # Social links 77 | social_links = [ 78 | { platform = "email", user_id = "johndoe@example.com" }, 79 | { platform = "github", user_id = "johndoe" }, 80 | { platform = "linkedin", user_id = "johndoe" }, 81 | { platform = "feed", user_id = true }, 82 | { platform = "keyoxide", user_id = "aspe:host:XXXXXXX" }, 83 | # { platform = "facebook", user_id = "" }, 84 | # { platform = "instagram", user_id = "" }, 85 | # { platform = "pinterest", user_id = "" }, 86 | # { platform = "twitter", user_id = "" }, 87 | # { platform = "stackoverflow", user_id = "" }, 88 | # { platform = "youtube", user_id = "" }, 89 | # { platform = "telegram", user_id = "" }, 90 | ] 91 | 92 | # GitHub settings that are used to generate the edit page link 93 | [extra.github] 94 | username = "en9inerd" 95 | repo = "zola-hacker" 96 | 97 | # Giscus settings 98 | # Please refer to https://giscus.app/ for more information 99 | # and don't use the same repo_id and category_id as in the example below 100 | [extra.giscus] 101 | repo = "en9inerd/zola-hacker" 102 | repo_id = "R_kgDOMZOW5g" 103 | category = "Comments" 104 | category_id = "DIC_kwDOMZOW5s4ChFNw" 105 | mapping = "og:title" 106 | strict = 0 107 | reactions_enabled = 1 108 | emit_metadata = 0 109 | input_position = "top" 110 | theme = "dark" 111 | lang = "en" 112 | loading = "lazy" 113 | crossorigin = "anonymous" 114 | 115 | # PGP key 116 | [extra.pgp_key] 117 | url = "https://keys.openpgp.org/vks/v1/by-fingerprint/0000000000000000000000000000000000000000" 118 | fingerprint = "0000 0000 0000 0000 0000 0000 0000 0000 0000 0000" 119 | 120 | # JSON-LD metadata 121 | [extra.bio] 122 | gender = "male" 123 | employer_name = "Unknown" 124 | employer_url = "https://unknown.com" 125 | job_title = "Software Engineer" 126 | links = [ 127 | "https://github.com/johndoe", 128 | "https://linkedin.com/in/johndoe", 129 | "https://twitter.com/johndoe", 130 | ] 131 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hacker theme for Zola 2 | 3 | Zola Hacker is a minimalistic theme for Zola, inspired by the [Hacker theme](https://pages-themes.github.io/hacker/) for Jekyll. It is designed for developers who want to write blogs. 4 | 5 | ## Demo 6 | 7 | [Live Preview](https://zola-hacker.enginerd.io/). 8 | 9 | ## Requirements 10 | 11 | Before using the theme, you need to install the [Zola](https://www.getzola.org/documentation/getting-started/installation/) ≥ 0.19.1. 12 | 13 | ## Quick Start 14 | 15 | ```bash 16 | git clone git@github.com:en9inerd/zola-hacker.git 17 | cd zola-hacker 18 | zola serve 19 | # open http://127.0.0.1:1111/ in the browser 20 | ``` 21 | 22 | ## Installation 23 | 24 | Just earlier we showed you how to run the theme directly. Now we start to 25 | install the theme in an existing site step by step. 26 | 27 | ### Step 1: Create a new zola site 28 | 29 | ```bash 30 | zola init mysite 31 | ``` 32 | 33 | ### Step 2: Install Zola Hacker Theme 34 | 35 | Download this theme to your themes directory: 36 | 37 | ```bash 38 | cd mysite/themes 39 | git clone git@github.com:en9inerd/zola-hacker.git 40 | ``` 41 | 42 | Or install as a submodule: 43 | 44 | ```bash 45 | cd mysite 46 | git init # if your project is a git repository already, ignore this command 47 | git submodule add git@github.com:en9inerd/zola-hacker.git themes/hacker 48 | ``` 49 | 50 | ### Step 3: Configuration 51 | 52 | Enable the theme in your `config.toml` in the site directory: 53 | 54 | ```toml 55 | theme = "hacker" 56 | ``` 57 | 58 | Or copy the `config.toml` from the theme directory to your project's 59 | root directory: 60 | 61 | ```bash 62 | cp themes/hacker/config.toml config.toml 63 | ``` 64 | 65 | ### Step 4: Add new content 66 | 67 | You can copy the content from the theme directory to your project: 68 | 69 | ```bash 70 | cp -r themes/hacker/content . 71 | ``` 72 | 73 | You can modify or add new posts in the `content/posts`, `content/pages` or other 74 | content directories as needed. 75 | 76 | ### Step 5: Run the project 77 | 78 | Just run `zola serve` in the root path of the project: 79 | 80 | ```bash 81 | zola serve 82 | ``` 83 | 84 | Command will start the Zola development web server accessible by default at 85 | `http://127.0.0.1:1111`. Saved changes will live reload in the browser. 86 | 87 | ## Customisation 88 | 89 | You can customize your configurations, templates and content for yourself. Look 90 | at the `config.toml`, `theme.toml`, `content` files and templates files in this 91 | repo for an idea. 92 | 93 | ### Global Configuration 94 | 95 | There are some configuration options that you can customize in `config.toml`. 96 | 97 | #### Configuration options before `extra` options 98 | 99 | Set the tags taxonomy in the `config.toml`: 100 | 101 | ```toml 102 | taxonomies = [ 103 | { name = "tags" }, 104 | ] 105 | ``` 106 | 107 | #### Configuration options under the `extra` 108 | 109 | The following options should be under the `[extra]` in `config.toml` 110 | 111 | - `language_code` - set HTML file language (default to `en-US`) 112 | - `title_separator` - the separator to your site title, like `|` and `-` (defaults to `|`) 113 | - `logo` - path to the logo image 114 | - `google_analytics` - Google Analytics tracking ID 115 | - `timeformat` - the timeformat for the blog article published date 116 | - `timezone` - the timezone for the blog article published date 117 | - `edit_page` - whether to show the edit page in the github repo for your posts 118 | - `menu` - set the menu items for your site 119 | - `contact_form_script_id` - the script id for the contact form based on [Google Script](https://github.com/en9inerd/learn-to-send-email-via-google-script-html-no-server) 120 | - `[extra.github]` - set the GitHub metadata for your site 121 | - `[extra.giscus]` - set the Giscus settings for your site to enable the comments. Please use own settings from the [Giscus](https://giscus.app/) website 122 | - `[extra.opengraph]` - set the Open Graph metadata for your site 123 | - `[extra.pgp_key]` - set pgp key in the footer for certain pages 124 | - `social_links` - set the social media links in the footer 125 | ... 126 | 127 | ### Templates 128 | 129 | All pages are extend to the `base.html`, and you can customize them as need. 130 | 131 | ### Shortcodes 132 | 133 | The theme provides some shortcodes to help you write your content: 134 | 135 | `contact_form` 136 | The `contact_form` shortcode is based on [Google Apps Mail](https://github.com/en9inerd/learn-to-send-email-via-google-script-html-no-server) to send emails without a server. 137 | It depends on `contact_form_script_id` in the `config.toml`. 138 | 139 | ```markdown 140 | {{ contact_form() }} 141 | ``` 142 | 143 | `cv` 144 | The `cv` shortcode is used to display the CV in the page. Data for CV is stored in yaml format in the `data/cv` directory. 145 | 146 | ```markdown 147 | {{ cv() }} 148 | ``` 149 | 150 | `github_avatar` 151 | The `github_avatar` shortcode is used to display the GitHub avatar image. It depends on `extra.github.username` in the `config.toml`. Also, you can pass size as an argument. 152 | 153 | ```markdown 154 | {{ github_avatar(size=100) }} 155 | ``` 156 | 157 | `projects` 158 | The `projects` shortcode is used to display repositories from GitHub. It depends on `extra.github.username` in the `config.toml` and `extra.repo_names` in page front matter to filter the repositories. 159 | 160 | ```markdown 161 | {{ projects() }} 162 | ``` 163 | 164 | ## Reporting Issues 165 | 166 | We use GitHub Issues as the official bug tracker for the **Zola Hacker Theme**. Please 167 | search [existing issues](https://github.com/en9inerd/zola-hacker/issues). It’s 168 | possible someone has already reported the same problem. 169 | 170 | If your problem or idea is not addressed yet, [open a new issue](https://github.com/en9inerd/zola-hacker/issues/new). 171 | 172 | ## License 173 | 174 | **Zola Hacker Theme** is distributed under the terms of the 175 | [MIT license](https://github.com/en9inerd/zola-hacker/blob/master/LICENSE). 176 | -------------------------------------------------------------------------------- /templates/macros.html: -------------------------------------------------------------------------------- 1 | {%- macro page_in_list(page) -%} 2 |
3 |

{{ page.title }}

4 |
5 | 8 |
9 | 10 |
11 | {%- if page.summary %} 12 | {{ page.summary | safe }} 13 | {%- elif page.description %} 14 | {{ page.description | safe }} 15 | {%- elif page.content %} 16 | {{ page.content | safe }} 17 | {%- endif -%} 18 |
19 | 20 | 25 |
26 | {% endmacro page_in_list %} 27 | 28 | {% macro get_edit_url(page) %} 29 | {%- set url = ["https://github.com", config.extra.github.username, config.extra.github.repo, "edit/master/content" , page.relative_path] | join(sep="/" ) %} 30 | Improve this page 31 | {% endmacro get_edit_url %} 32 | 33 | {%- macro page_timestamp(page) -%} 34 | Posted on {{ page.date | date(format=config.extra.timeformat | default(value="%B %e, %Y at %H:%M %Z"), timezone=config.extra.timezone | default(value="America/New_York")) }} 35 | {%- endmacro page_timestamp %} 36 | 37 | {%- macro seo( 38 | title="", 39 | title_addition="", 40 | description="", 41 | type="website", 42 | is_home=false, 43 | is_404=false, 44 | is_page=false 45 | ) 46 | %} 47 | 48 | {%- if page.date %} 49 | {%- set created_date = page.date | date(format="%+") %} 50 | {%- endif %} 51 | 52 | {%- if is_404 %} 53 | 54 | {%- else %} 55 | 56 | 57 | 58 | {%- endif %} 59 | {%- if current_url %} 60 | {%- set page_url = current_url %} 61 | {%- else %} 62 | {%- set page_url = get_url(path="404.html") %} 63 | {%- endif %} 64 | {%- if current_path %} 65 | {%- set page_path = current_path %} 66 | {%- else %} 67 | {%- set page_path = "/404.html" %} 68 | {%- endif %} 69 | 70 | {{ title ~ title_addition }} 71 | 72 | 73 | 74 | 75 | 76 | {%- if config.extra.opengraph %} 77 | 78 | {%- if page.extra.images %} 79 | {%- for image in page.extra.images %} 80 | 81 | {%- endfor %} 82 | {%- elif section.extra.images %} 83 | {%- for image in section.extra.images %} 84 | 85 | {%- endfor %} 86 | {%- elif config.extra.logo %} 87 | 88 | {%- endif %} 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | {%- if page.extra.images %} 98 | {%- for image in page.extra.images %} 99 | 100 | {%- endfor %} 101 | {%- elif section.extra.images %} 102 | {%- for image in section.extra.images %} 103 | 104 | {%- endfor %} 105 | {%- elif config.extra.logo %} 106 | 107 | {%- endif %} 108 | 109 | 110 | 111 | {%- if page.date %} 112 | 113 | {%- endif %} 114 | {%- endif %} 115 | 116 | {%- if is_home %} 117 | 139 | {%- endif %} 140 | 141 | {%- if is_page %} 142 | {%- if page.extra.seo.type is defined and page.extra.seo.type == "Organization" %} 143 | 159 | {%- endif %} 160 | {%- if page.extra.seo.type is defined and page.extra.seo.type == "Person" %} 161 | 189 | {%- endif %} 190 | {%- if page.relative_path is defined and page.relative_path is starting_with("posts/") %} 191 | 221 | {%- else %} 222 | 235 | {%- endif %} 236 | {%- endif %} 237 | {%- endmacro %} 238 | -------------------------------------------------------------------------------- /sass/main.scss: -------------------------------------------------------------------------------- 1 | @import "default_colors"; 2 | @import "svg-icons"; 3 | @import "primary-font"; 4 | 5 | $body-background: $cod-grey !default; 6 | $body-foreground: $gallery !default; 7 | $header: $amber !default; 8 | $blockquote-color: $silver-chalice !default; 9 | $blockquote-border: $dove-grey !default; 10 | $container-max-width: 1000px; 11 | 12 | @mixin media-max-width($max-width) { 13 | @media (max-width: $max-width) { 14 | @content; 15 | } 16 | } 17 | 18 | body { 19 | margin: 0; 20 | padding: 0; 21 | background: $body-background 0 0; 22 | color: $body-foreground; 23 | font-size: 16px; 24 | line-height: 1.5; 25 | font-family: "JetBrains Mono", monospace; 26 | font-feature-settings: 27 | "calt" 0, 28 | "zero" 1, 29 | "cv12" 1, 30 | "cv18" 1, 31 | "cv19" 1, 32 | "cv20" 1; 33 | } 34 | 35 | /* General & 'Reset' Stuff */ 36 | 37 | .container { 38 | width: 90%; 39 | max-width: $container-max-width; 40 | margin: 0 auto; 41 | } 42 | 43 | h1, 44 | h2, 45 | h3, 46 | h4, 47 | h5, 48 | h6 { 49 | margin: 0 0 20px; 50 | } 51 | 52 | li { 53 | line-height: 1.4; 54 | } 55 | 56 | /* Header,
57 | header - container 58 | h1 - project name 59 | h2 - project description 60 | */ 61 | 62 | header { 63 | background: rgba(0, 0, 0, 0.1); 64 | width: 100%; 65 | border-bottom: 1px dashed $amber; //header; 66 | padding: 20px 0; 67 | margin: 0 0 40px 0; 68 | } 69 | 70 | header h1 { 71 | font-size: 30px; 72 | line-height: 1.5; 73 | margin: 0 0 0 -40px; 74 | font-weight: bold; 75 | color: $amber; //$header; 76 | text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1), 77 | 0 0 5px rgba(255, 191, 0, 0.1), 78 | 0 0 10px rgba(255, 191, 0, 0.1); 79 | letter-spacing: -1px; 80 | 81 | @include media-max-width($container-max-width) { 82 | margin-left: 0; 83 | } 84 | } 85 | 86 | header h1:before { 87 | content: "./ "; 88 | font-size: 24px; 89 | } 90 | 91 | header h2 { 92 | font-size: 18px; 93 | font-weight: 300; 94 | color: $light-grey; 95 | } 96 | 97 | nav { 98 | display: flex; 99 | justify-content: center; 100 | flex-wrap: wrap; 101 | gap: 10px; 102 | margin: 30px 0 20px 0; 103 | } 104 | 105 | .smaller { 106 | font-size: 0.95em; 107 | } 108 | 109 | /* Main Content 110 | */ 111 | 112 | main { 113 | width: 100%; 114 | } 115 | 116 | main img { 117 | max-width: 100% 118 | } 119 | 120 | h1, 121 | h2, 122 | h3, 123 | h4, 124 | h5, 125 | h6 { 126 | font-weight: normal; 127 | color: $header; 128 | letter-spacing: -0.03em; 129 | text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1), 130 | 0 0 5px rgba(255, 191, 0, 0.1), 131 | 0 0 10px rgba(255, 191, 0, 0.1); 132 | } 133 | 134 | main h1 { 135 | font-size: 30px; 136 | } 137 | 138 | main h2 { 139 | font-size: 24px; 140 | } 141 | 142 | main h3 { 143 | font-size: 18px; 144 | } 145 | 146 | main h4 { 147 | font-size: 16px; 148 | } 149 | 150 | main h5 { 151 | font-size: 12px; 152 | text-transform: uppercase; 153 | margin: 0 0 5px 0; 154 | } 155 | 156 | main h6 { 157 | font-size: 12px; 158 | text-transform: uppercase; 159 | color: $light-grey; 160 | margin: 0 0 5px 0; 161 | } 162 | 163 | dt { 164 | font-style: italic; 165 | font-weight: bold; 166 | } 167 | 168 | ul li { 169 | list-style-type: '>> '; 170 | 171 | &::marker { 172 | color: $amber; 173 | } 174 | } 175 | 176 | blockquote { 177 | color: $blockquote-color; 178 | padding-left: 10px; 179 | border-left: 1px dotted $blockquote-border; 180 | } 181 | 182 | code:not(pre code) { 183 | background: #2b2c2f; 184 | // border: 1px solid rgba(255, 255, 255, 0.15); 185 | padding: 0px 3px; 186 | margin: 0px -3px; 187 | color: $bouquet; 188 | border-radius: 2px; 189 | } 190 | 191 | pre { 192 | padding: 1rem; 193 | overflow: auto; 194 | border-radius: 2px; 195 | 196 | &[data-linenos] { 197 | padding: 1rem 0; 198 | } 199 | 200 | table { 201 | width: 100%; 202 | border-collapse: collapse; 203 | margin: 0; 204 | 205 | td { 206 | padding: 0; 207 | 208 | &:nth-of-type(1) { 209 | text-align: center; 210 | user-select: none; 211 | width: 3.5rem; 212 | } 213 | } 214 | } 215 | 216 | mark { 217 | display: block; 218 | background-color: rgba(254, 252, 232, 0.9); 219 | } 220 | } 221 | 222 | table { 223 | width: 100%; 224 | margin: 0 0 20px 0; 225 | } 226 | 227 | th { 228 | border-bottom: 1px dashed $amber; 229 | padding: 5px 10px; 230 | } 231 | 232 | td { 233 | padding: 5px 10px; 234 | } 235 | 236 | hr { 237 | height: 0; 238 | border: 0; 239 | border-bottom: 1px dashed $amber; 240 | color: $amber; 241 | } 242 | 243 | /* Buttons 244 | */ 245 | 246 | .btn { 247 | display: inline-block; 248 | background: -webkit-linear-gradient(top, rgba(40, 40, 40, 0.3), rgba(35, 35, 35, 0.3) 50%, rgba(10, 10, 10, 0.3) 50%, rgba(0, 0, 0, 0.3)); 249 | padding: 8px 18px; 250 | border-radius: 50px; 251 | border: 2px solid rgba(0, 0, 0, 0.7); 252 | border-bottom: 2px solid rgba(0, 0, 0, 0.7); 253 | border-top: 2px solid rgba(0, 0, 0, 1); 254 | color: rgba(255, 255, 255, 0.8); 255 | font-family: Helvetica, Arial, sans-serif; 256 | // font-weight: bold; 257 | font-size: 14px; 258 | text-decoration: none; 259 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.75); 260 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05); 261 | } 262 | 263 | .btn:hover { 264 | background: -webkit-linear-gradient(top, rgba(40, 40, 40, 0.6), rgba(35, 35, 35, 0.6) 50%, rgba(10, 10, 10, 0.8) 50%, rgba(0, 0, 0, 0.8)); 265 | } 266 | 267 | .btn .icon { 268 | display: inline-block; 269 | width: 16px; 270 | height: 16px; 271 | vertical-align: sub; 272 | margin-right: 8px; 273 | } 274 | 275 | .btn-github .icon { 276 | opacity: 0.6; 277 | background: url("/images/blacktocat.png") 0 0 no-repeat; 278 | } 279 | 280 | /* Links 281 | a, a:hover, a:visited 282 | */ 283 | 284 | a { 285 | color: #63c0f5; 286 | text-shadow: 0 0 5px rgba(104, 182, 255, 0.5); 287 | outline: none; 288 | text-decoration: none; 289 | } 290 | 291 | main, 292 | footer { 293 | 294 | a:hover, 295 | a:active { 296 | text-shadow: 0 0 5px rgba(104, 182, 255, 1.0); 297 | } 298 | 299 | a.btn:hover, 300 | a.btn:active { 301 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.75); 302 | } 303 | } 304 | 305 | #a-title h1 { 306 | 307 | /* Clearfix */ 308 | & { 309 | text-decoration: none; 310 | } 311 | 312 | &:hover { 313 | text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3), 314 | 0 0 5px rgba(255, 191, 0, 0.3), 315 | 0 0 10px rgba(255, 191, 0, 0.3); 316 | } 317 | } 318 | 319 | /* Pages/Posts 320 | */ 321 | 322 | .page-info { 323 | margin-bottom: 2em; 324 | } 325 | 326 | .page-info span { 327 | font-weight: normal; 328 | color: $amber; 329 | letter-spacing: -0.03em; 330 | text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1), 331 | 0 0 5px rgba(255, 191, 0, 0.1), 332 | 0 0 10px rgba(255, 191, 0, 0.1); 333 | } 334 | 335 | .comments { 336 | margin-top: 3rem; 337 | } 338 | 339 | .pages>.page { 340 | padding-bottom: 2em; 341 | margin-bottom: 1.8em; 342 | border-bottom: 1px dashed $amber; 343 | } 344 | 345 | .pages>.page:last-child { 346 | padding-bottom: 1em; 347 | border-bottom: none; 348 | } 349 | 350 | .page { 351 | .page-footer { 352 | text-align: right; 353 | } 354 | 355 | .read-more { 356 | text-transform: uppercase; 357 | margin-left: 15px; 358 | } 359 | } 360 | 361 | .header-container { 362 | display: flex; 363 | justify-content: space-between; 364 | } 365 | 366 | .improve { 367 | font-size: 14px; 368 | white-space: nowrap; 369 | margin-left: 1.5rem; 370 | 371 | a { 372 | color: $light-grey; 373 | } 374 | } 375 | 376 | @media screen and (max-width: 568px) { 377 | .hide-on-mobiles { 378 | display: none !important; 379 | } 380 | } 381 | 382 | /* Date and time 383 | */ 384 | 385 | time { 386 | opacity: 0.6; 387 | } 388 | 389 | .page time { 390 | display: block; 391 | } 392 | 393 | time.page-time { 394 | margin-top: 1em; 395 | } 396 | 397 | /* Pagination 398 | */ 399 | 400 | .pagination { 401 | padding-top: 20px; 402 | text-align: center; 403 | border-top: 1px dashed $amber; 404 | color: $amber; 405 | 406 | .previous-page { 407 | margin-right: 2em; 408 | } 409 | 410 | .next-page { 411 | margin-left: 2em; 412 | } 413 | } 414 | 415 | /* Footer 416 | */ 417 | 418 | footer { 419 | margin: 3.5rem 0 2rem; 420 | text-align: center; 421 | } 422 | 423 | .copyright { 424 | color: $light-grey; 425 | display: block; 426 | margin-top: 15px; 427 | font-size: 14px; 428 | } 429 | 430 | .pgp-key { 431 | font-size: 85%; 432 | 433 | code { 434 | padding: 2px 4px; 435 | font-size: 90%; 436 | color: #c7254e; 437 | background-color: black; 438 | border-radius: 4px; 439 | font-family: Menlo, Monaco, Consolas, "Courier New", monospace; 440 | } 441 | 442 | a { 443 | text-shadow: none; 444 | } 445 | 446 | a:hover code, 447 | a:active code { 448 | text-shadow: 0 0 5px #99092d; 449 | } 450 | } 451 | 452 | /* Feedback form 453 | */ 454 | 455 | input[type=text], 456 | input[type=email], 457 | textarea { 458 | width: 100%; 459 | max-width: 820px; 460 | box-sizing: border-box; 461 | background: rgba(0, 0, 0, 0.9); 462 | border: 1px solid rgba(255, 255, 255, 0.15); 463 | padding: 10px; 464 | font-size: 16px; 465 | color: $amber; 466 | border-radius: 2px; 467 | resize: none; 468 | margin-bottom: 6px; 469 | } 470 | 471 | input[type=text]:focus, 472 | input[type=email]:focus, 473 | textarea:focus { 474 | outline: none; 475 | border: 1px solid $chelsea-cucumber; 476 | } 477 | 478 | :not(output):user-invalid { 479 | border: 1px solid red; 480 | border-radius: 2px; 481 | box-shadow: none; 482 | } 483 | 484 | .contact-form { 485 | overflow: hidden; 486 | max-width: 820px; 487 | margin: 2.5rem auto 0; 488 | } 489 | 490 | .contact-form textarea { 491 | height: 150px; 492 | } 493 | 494 | .contact-form .submit-btn-align { 495 | text-align: right; 496 | } 497 | 498 | #itsatrap { 499 | display: none; 500 | } 501 | 502 | .thankyou-message { 503 | display: none; 504 | } 505 | 506 | /* Curriculum Vitae */ 507 | 508 | .cv { 509 | h2:not(:first-of-type) { 510 | margin-top: 1.8em; 511 | } 512 | 513 | .cv-experience, 514 | .cv-projects, 515 | .cv-education { 516 | h4 { 517 | margin-bottom: 0; 518 | } 519 | 520 | h3:not(:first-of-type) { 521 | margin-top: 1.8em; 522 | } 523 | } 524 | 525 | h3 { 526 | margin-bottom: 0.55em; 527 | } 528 | } 529 | 530 | .cv-btn { 531 | margin-top: -0.3rem; 532 | margin-bottom: -1.2rem; 533 | text-align: right; 534 | } 535 | 536 | @media screen and (max-width: 568px) { 537 | .cv-btn { 538 | margin-top: 0.2rem; 539 | margin-bottom: -2.3rem; 540 | } 541 | } 542 | 543 | /* Tags */ 544 | 545 | .tags h2 { 546 | margin-top: 1.6em; 547 | } 548 | --------------------------------------------------------------------------------