Rewrite site with 11ty
This commit is contained in:
parent
815d89ad49
commit
3da1f74f98
9
.editorconfig
Normal file
9
.editorconfig
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.{html,njk,js,scss,css}]
|
||||||
|
indent_style = tab
|
||||||
|
trim_trailing_whitespace = true
|
61
.eleventy.js
Normal file
61
.eleventy.js
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
const eleventyNavigationPlugin = require("@11ty/eleventy-navigation");
|
||||||
|
const eleventySass = require("eleventy-sass");
|
||||||
|
const mdDefList = require("markdown-it-deflist");
|
||||||
|
|
||||||
|
module.exports = function (eleventyConfig) {
|
||||||
|
eleventyConfig.setBrowserSyncConfig({
|
||||||
|
files: "./_site/css/**/*.css",
|
||||||
|
});
|
||||||
|
eleventyConfig.addPassthroughCopy({
|
||||||
|
"node_modules/bootstrap-icons/bootstrap-icons.svg":
|
||||||
|
"images/bootstrap-icons.svg",
|
||||||
|
"node_modules/@fortawesome/fontawesome-free/sprites":
|
||||||
|
"images/fontawesome",
|
||||||
|
"src/js/site.js": "js/site.js"
|
||||||
|
});
|
||||||
|
eleventyConfig.addPlugin(eleventyNavigationPlugin);
|
||||||
|
eleventyConfig.addPlugin(eleventySass, {
|
||||||
|
sass: {
|
||||||
|
loadPaths: ["node_modules"],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
eleventyConfig.amendLibrary("md", mdLib => mdLib.use(mdDefList));
|
||||||
|
|
||||||
|
eleventyConfig.addFilter("IsNotPage", (collection, url) =>
|
||||||
|
collection.filter(item => item.url != url));
|
||||||
|
eleventyConfig.addFilter("IsMainPageSection", (collection) => {
|
||||||
|
return collection.filter(item => !item.url || item.url.startsWith("/_sections"));
|
||||||
|
});
|
||||||
|
eleventyConfig.addFilter("IsNotMainPageSection", (collection) => {
|
||||||
|
return collection.filter(item => item.url != null && (item.data.tags == null || !item.data.tags.includes("MainPage")));
|
||||||
|
});
|
||||||
|
eleventyConfig.addFilter("orderBySectionOrder", (collection) =>
|
||||||
|
collection.sort((a, b) => a.data.sectionOrder - b.data.sectionOrder)
|
||||||
|
);
|
||||||
|
eleventyConfig.addFilter("filterDrafts", collection => {
|
||||||
|
if (process.env.BUILD_DRAFTS === true) {
|
||||||
|
console.log("Skipping filtering drafts");
|
||||||
|
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
return collection.filter(item => {
|
||||||
|
console.log(item);
|
||||||
|
|
||||||
|
if (item.data.draft != null) {
|
||||||
|
return !item.data.draft;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
eleventyConfig.addFilter("log", (value) => console.log(value));
|
||||||
|
|
||||||
|
console.log("BUILD_DRAFTS: " + process.env.BUILD_DRAFTS);
|
||||||
|
|
||||||
|
return {
|
||||||
|
dir: {
|
||||||
|
input: "src",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
33
.gitignore
vendored
33
.gitignore
vendored
|
@ -1,4 +1,29 @@
|
||||||
static/lib/
|
# Node.js files
|
||||||
static/info.php
|
node_modules/
|
||||||
.vscode/
|
|
||||||
public/
|
# Built files
|
||||||
|
/_site/
|
||||||
|
|
||||||
|
# MacOS files
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
3
.nova/Configuration.json
Normal file
3
.nova/Configuration.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
21
.nova/Tasks/Eleventy.json
Normal file
21
.nova/Tasks/Eleventy.json
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"actions" : {
|
||||||
|
"build" : {
|
||||||
|
"enabled" : true,
|
||||||
|
"script" : "npm install\nnpm run build"
|
||||||
|
},
|
||||||
|
"clean" : {
|
||||||
|
"enabled" : true,
|
||||||
|
"script" : "rm -r _site\nnpm clean-install"
|
||||||
|
},
|
||||||
|
"run" : {
|
||||||
|
"enabled" : true,
|
||||||
|
"script" : "npm install\nnpm start"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"environment" : {
|
||||||
|
"BUILD_DRAFTS" : "false"
|
||||||
|
},
|
||||||
|
"openLogOnRun" : "fail",
|
||||||
|
"persistent" : true
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
# Personal Website
|
# Personal Site
|
||||||
|
|
||||||
The files for my personal site at [neilbrommer.com](https://neilbrommer.com).
|
The source for my personal website at [neilbrommer.com](https://neilbrommer.com).
|
||||||
|
|
||||||
Built using [Hugo](https://gohugo.io/), [Bootstrap 4](https://getbootstrap.com/) and [FontAwesome 5](https://fontawesome.com/).
|
Built using [11ty](https://www.11ty.dev/).
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
+++
|
|
||||||
title = "{{ replace .Name '-' ' ' | title }}"
|
|
||||||
date = {{ .Date }}
|
|
||||||
draft = true
|
|
||||||
+++
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
languageCode = "en-us"
|
|
||||||
title = "Neil Brommer"
|
|
||||||
baseURL = "https://neilbrommer.com/"
|
|
||||||
enableGitInfo = true
|
|
||||||
enableRobotsTXT = true
|
|
||||||
googleAnalytics = "UA-105178749-1"
|
|
|
@ -1,11 +0,0 @@
|
||||||
+++
|
|
||||||
title = "Neil Brommer"
|
|
||||||
date = 2018-05-12T09:37:22-07:00
|
|
||||||
draft = false
|
|
||||||
[menu]
|
|
||||||
[menu.main]
|
|
||||||
title = "Home"
|
|
||||||
weight = -100
|
|
||||||
+++
|
|
||||||
|
|
||||||
Recent graduate from <a href="https://www.ewu.edu/" target="_blank">Eastern Washington University</a> with a Bachelor of Science in Computer Science
|
|
|
@ -1,44 +0,0 @@
|
||||||
+++
|
|
||||||
title = "Contact"
|
|
||||||
date = 2018-05-12T11:58:21-07:00
|
|
||||||
draft = false
|
|
||||||
[menu]
|
|
||||||
[menu.main]
|
|
||||||
weight = -70
|
|
||||||
+++
|
|
||||||
|
|
||||||
<div id="successAlert" class="alert alert-success" role="alert" style="display: none;">Message sent successfully!</div>
|
|
||||||
<div id="errorAlert" class="alert alert-danger" role="alert" style="display: none"></div>
|
|
||||||
|
|
||||||
<form id="contactForm" class="jumbotron border rounded p-1 p-sm-4">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="nameField">Name
|
|
||||||
<span class="required">*</span>
|
|
||||||
</label>
|
|
||||||
<input id="nameField" type="text" required="required" class="form-control">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="emailField">Email Address
|
|
||||||
<span class="required">*</span>
|
|
||||||
</label>
|
|
||||||
<input id="emailField" type="email" required="required" class="form-control">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="subjectField">Subject</label>
|
|
||||||
<input id="subjectField" type="text" class="form-control">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="messageField">Message
|
|
||||||
<span class="required">*</span>
|
|
||||||
</label>
|
|
||||||
<textarea id="messageField" rows="5" required="required" class="form-control"></textarea>
|
|
||||||
</div>
|
|
||||||
<div class="form-group captcha">
|
|
||||||
<div class="g-recaptcha" data-sitekey="6LfFATcUAAAAAJ_YZ3qvYWqrtOUHmCItq-azzV3x"></div>
|
|
||||||
</div>
|
|
||||||
<button id="contactSubmit" type="submit" class="btn btn-primary">Submit</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
|
|
||||||
<script src='https://www.google.com/recaptcha/api.js'></script>
|
|
||||||
<script src="/js/contact.js"></script>
|
|
|
@ -1,11 +0,0 @@
|
||||||
+++
|
|
||||||
title = "Links"
|
|
||||||
date = 2018-05-12T14:28:21-07:00
|
|
||||||
draft = false
|
|
||||||
description = "A list of links (mostly to articles) that I find useful or interesting."
|
|
||||||
[menu]
|
|
||||||
[menu.main]
|
|
||||||
weight = -80
|
|
||||||
+++
|
|
||||||
|
|
||||||
{{< linkCardList >}}
|
|
|
@ -1,91 +0,0 @@
|
||||||
+++
|
|
||||||
title = "Resume"
|
|
||||||
date = 2018-05-12T11:48:46-07:00
|
|
||||||
draft = false
|
|
||||||
description = """
|
|
||||||
References and more contact information available on request via the [contact page](/contact).
|
|
||||||
|
|
||||||
PDF version available [here](/NeilBrommer-WebResume.pdf)"""
|
|
||||||
[menu]
|
|
||||||
[menu.main]
|
|
||||||
weight = -90
|
|
||||||
+++
|
|
||||||
|
|
||||||
## Education
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-8">
|
|
||||||
<b>Eastern Washington University</b>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-4">
|
|
||||||
<b class="float-sm-right">Cheney, Washington</b>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-8">
|
|
||||||
<i>Bachelor of Science in Computer Science, 3.52 GPA</i>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-4">
|
|
||||||
<i class="float-sm-right">2014 – 2018</i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-8">
|
|
||||||
<b>Spokane Community College</b>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-4">
|
|
||||||
<b class="float-sm-right">Spokane, Washington</b>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-8">
|
|
||||||
<i>Associate of Applied Science in Network Design and Administration, 3.13 GPA</i>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-4">
|
|
||||||
<i class="float-sm-right">2011 – 2013</i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
Networking with Cisco switches and routers, with basic Windows and Linux administration
|
|
||||||
</p>
|
|
||||||
|
|
||||||
|
|
||||||
## Relevant Coursework and Skills
|
|
||||||
|
|
||||||
<ul class="row">
|
|
||||||
<li class="col-md-6">Object-Oriented Languages – Java, C#, C++</li>
|
|
||||||
<li class="col-md-6">Object-Oriented design patterns</li>
|
|
||||||
<li class="col-md-6">GUI Programming – JavaFX, WinForms, WPF</li>
|
|
||||||
<li class="col-md-6">Databases – SQL, Database Design</li>
|
|
||||||
<li class="col-md-6">Web Development – HTML, CSS, JavaScript, JQuery, PHP, REST APIs</li>
|
|
||||||
<li class="col-md-6">Operating Systems – C, Unix, threads and processes, memory management</li>
|
|
||||||
<li class="col-md-6">Software Engineering</li>
|
|
||||||
<li class="col-md-6">Version Control Systems – Git</li>
|
|
||||||
<li class="col-md-6">Network Programming – Network sockets, binary protocols</li>
|
|
||||||
<li class="col-md-6">Data Mining</li>
|
|
||||||
<li class="col-md-6">Linux Administration – Basic system maintenance, Email, DNS</li>
|
|
||||||
<li class="col-md-6">Windows Administration – MS Exchange, Active Directory</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
|
|
||||||
## Projects
|
|
||||||
|
|
||||||
See the [projects](/#projects) list
|
|
||||||
|
|
||||||
|
|
||||||
## Certifications
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<div style="font-weight: bold;">CCNA</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-8">
|
|
||||||
<i>Cisco Certified Network Administrator</i>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-4">
|
|
||||||
<i class="float-sm-right">September 2013 – 2016</i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
Cisco’s second level of computer networking certifications. Focuses on designing and managing routed and switched networks.
|
|
||||||
</p>
|
|
|
@ -1,54 +0,0 @@
|
||||||
[[link]]
|
|
||||||
title = "Ok→Cancel versus Cancel→Ok"
|
|
||||||
description = "A look at button ordering in GUIs from the Factorio blog"
|
|
||||||
URL = "https://www.factorio.com/blog/post/fff-246"
|
|
||||||
|
|
||||||
[[link]]
|
|
||||||
title = "Principles of Lighting and Rendering with John Carmack at QuakeCon 2013"
|
|
||||||
description = "John Carmack talking about various 3D rendering concepts."
|
|
||||||
URL = "https://www.youtube.com/watch?v=IyUgHPs86XM"
|
|
||||||
|
|
||||||
[[link]]
|
|
||||||
title = "Why the Flash Crash Really Matters"
|
|
||||||
description = "An article on the flash crash of 2010 and the <a href=\"https://en.wikipedia.org/wiki/High-frequency_trading\" target=\"_blank\">high-frequency trading</a> programs that caused it."
|
|
||||||
URL = "http://nautil.us/issue/23/dominoes/why-the-flash-crash-really-matters"
|
|
||||||
|
|
||||||
[[link]]
|
|
||||||
title = "Algorithms and Bias: Q. and A. With Cynthia Dwork"
|
|
||||||
description = "An interview with Cynthia Dwork of Microsoft Research on how algorithms show bias and where those biases come from."
|
|
||||||
URL = "https://www.nytimes.com/2015/08/11/upshot/algorithms-and-bias-q-and-a-with-cynthia-dwork.html?abt=0002&abg=1"
|
|
||||||
|
|
||||||
[[link]]
|
|
||||||
title = "Origin of Quake3’s Fast InvSqrt()"
|
|
||||||
description = "An article on the inverse square root function in Quake 3 looking into how it works and its history."
|
|
||||||
URL = "https://www.beyond3d.com/content/articles/8/"
|
|
||||||
|
|
||||||
[[link]]
|
|
||||||
title = "Real Time Rendering: An Overview for Artists"
|
|
||||||
description = "A look at various aspects of real time 3D rendering, especially in video games."
|
|
||||||
URL = "https://jesshiderue4.wordpress.com/real-time-rendering-an-overview-for-artists/"
|
|
||||||
|
|
||||||
[[link]]
|
|
||||||
title = "Entering the Quantum Era—How Firefox got fast again and where it’s going to get faster"
|
|
||||||
description = "An article that looks at various architectural changes to Firefox in version 57 and changes planned for future versions."
|
|
||||||
URL = "https://hacks.mozilla.org/2017/11/entering-the-quantum-era-how-firefox-got-fast-again-and-where-its-going-to-get-faster/"
|
|
||||||
|
|
||||||
[[link]]
|
|
||||||
title = "The whole web at maximum FPS: How WebRender gets rid of jank "
|
|
||||||
description = " An article that gives a high level description how Firefox’s new rendering engine works."
|
|
||||||
URL = "https://hacks.mozilla.org/2017/10/the-whole-web-at-maximum-fps-how-webrender-gets-rid-of-jank/"
|
|
||||||
|
|
||||||
[[link]]
|
|
||||||
title = "Inside a super fast CSS engine: Quantum CSS (aka Stylo)"
|
|
||||||
description = "A high level overview of how Firefox’s new CSS engine Stylo works."
|
|
||||||
URL = "https://hacks.mozilla.org/2017/08/inside-a-super-fast-css-engine-quantum-css-aka-stylo/"
|
|
||||||
|
|
||||||
[[link]]
|
|
||||||
title = "The Judge’s Code"
|
|
||||||
description = "An article on Judge William Alsup, who was the judge for the first <a href=\"https://en.wikipedia.org/wiki/Oracle_America,_Inc._v._Google,_Inc.\" target=\"_blank\"> Oracle v. Google</a> case over whether an API can be copywritten, and is now the judge for the <a href=\"http://www.businessinsider.com/google-waymo-v-uber-case-explained-2017-5\" target=\"_blank\">Waymo (Google) v. Uber</a>."
|
|
||||||
URL = "https://www.theverge.com/2017/10/19/16503076/oracle-vs-google-judge-william-alsup-interview-waymo-uber"
|
|
||||||
|
|
||||||
[[link]]
|
|
||||||
title = "LinkedIn Dark Patterns"
|
|
||||||
description = "An in depth blog post detailing how LinkedIn used dark patterns to trick users."
|
|
||||||
URL = "https://schlosser.io/writing/linkedin-dark-patterns/"
|
|
|
@ -1,78 +0,0 @@
|
||||||
[[project]]
|
|
||||||
title = "Growing Neighbors"
|
|
||||||
incomplete = false
|
|
||||||
description = "The website for the Growing Neighbors organization in Spokane, WA. I worked as part of a team building this site."
|
|
||||||
[[project.links]]
|
|
||||||
title = "Site"
|
|
||||||
external = true
|
|
||||||
URL = "https://growingneighbors.org/"
|
|
||||||
|
|
||||||
[[project]]
|
|
||||||
title = "Snippets"
|
|
||||||
incomplete = true
|
|
||||||
description = "A web based tool for managing reusable pieces of code and useful programming resources"
|
|
||||||
[[project.links]]
|
|
||||||
title = "Web Client"
|
|
||||||
external = true
|
|
||||||
URL = "https://snippets.neilbrommer.com/"
|
|
||||||
[[project.links]]
|
|
||||||
title = "Documentation"
|
|
||||||
external = true
|
|
||||||
URL = "https://snippets.neilbrommer.com/documentation.html"
|
|
||||||
[[project.links]]
|
|
||||||
title = "Source Code"
|
|
||||||
external = true
|
|
||||||
URL = "https://github.com/NeilBrommer/SnippetManager"
|
|
||||||
|
|
||||||
[[project]]
|
|
||||||
title = "Start"
|
|
||||||
incomplete = false
|
|
||||||
description = "A new tab page that displays lists of links using the browser's indexedDB to store all data"
|
|
||||||
[[project.links]]
|
|
||||||
title = "Site"
|
|
||||||
external = true
|
|
||||||
URL = "https://start.neilbrommer.com/"
|
|
||||||
[[project.links]]
|
|
||||||
title = "Source Code"
|
|
||||||
external = true
|
|
||||||
URL = "https://github.com/NeilBrommer/NewTabPage"
|
|
||||||
|
|
||||||
[[project]]
|
|
||||||
title = "Picture Viewer"
|
|
||||||
incomplete = false
|
|
||||||
description = "A Windows program for viewing image files written using C# and WPF"
|
|
||||||
[[project.links]]
|
|
||||||
title = "Source Code"
|
|
||||||
external = true
|
|
||||||
URL = "https://github.com/NeilBrommer/PictureViewer"
|
|
||||||
|
|
||||||
[[project]]
|
|
||||||
title = "Website"
|
|
||||||
incomplete = false
|
|
||||||
description = "The source code for this website"
|
|
||||||
[[project.links]]
|
|
||||||
title = "Source Code"
|
|
||||||
external = true
|
|
||||||
URL = "https://github.com/NeilBrommer/Personal-Site"
|
|
||||||
|
|
||||||
[[project]]
|
|
||||||
title = "Resume"
|
|
||||||
incomplete = false
|
|
||||||
description = "My [Resume](resume)"
|
|
||||||
[[project.links]]
|
|
||||||
title = "PDF"
|
|
||||||
external = true
|
|
||||||
URL = "/NeilBrommer-WebResume.pdf"
|
|
||||||
[[project.links]]
|
|
||||||
title = "Source Code"
|
|
||||||
external = true
|
|
||||||
URL = "https://github.com/NeilBrommer/Resume"
|
|
||||||
|
|
||||||
[[project]]
|
|
||||||
title = "Auto Dark"
|
|
||||||
incomplete = false
|
|
||||||
description = "A small utility for setting/toggling the Windows 10 dark theme. Useful in combination with the Windows Task Scheduler to automatically change the theme."
|
|
||||||
[[project.links]]
|
|
||||||
title = "Source Code"
|
|
||||||
external = true
|
|
||||||
URL = "https://github.com/NeilBrommer/WindowsAutoDark"
|
|
|
@ -1,2 +0,0 @@
|
||||||
github = "https://github.com/NeilBrommer"
|
|
||||||
linkedin = "https://www.linkedin.com/in/neilbrommer/"
|
|
|
@ -1,22 +0,0 @@
|
||||||
{{ partial "htmlhead" . }}
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
{{ partial "navbar" .}}
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
{{ if isset .Params "description" }}
|
|
||||||
<div class="jumbotron">
|
|
||||||
<h1 id="title" class="display-4 mb-4">{{ .Title }}</h1>
|
|
||||||
|
|
||||||
{{ .Params.description | markdownify }}
|
|
||||||
</div>
|
|
||||||
{{ else }}
|
|
||||||
<h1 class="display-4 mb-4">{{ .Title }}</h1>
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{.Content}}
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
|
@ -1,26 +0,0 @@
|
||||||
{{ partial "htmlhead" . }}
|
|
||||||
<link href="/css/index.css" rel="stylesheet">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
{{ partial "navbar" . }}
|
|
||||||
|
|
||||||
<div id="header" class="text-center">
|
|
||||||
<div id="headerContent" class="container">
|
|
||||||
<h1 id="title" class="display-4 font-weight-normal">{{ .Title }}</h1>
|
|
||||||
<div class="lead"> {{ .Content }} </div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<h1 id="projects" class="mb-4">Projects</h1>
|
|
||||||
|
|
||||||
<div class="card-columns">
|
|
||||||
{{ range .Site.Data.projects.project }}
|
|
||||||
{{ partial "projectCard" . }}
|
|
||||||
{{ end }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
|
@ -1,41 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
|
|
||||||
<html lang='{{ .Site.Language.Lang | default "en-us" }}'>
|
|
||||||
<head>
|
|
||||||
<title>{{ if eq (.Site.Title) (.Title) }}{{ .Site.Title }}{{ else }}{{ .Site.Title }} · {{ .Title }}{{ end }}</title>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
|
|
||||||
|
|
||||||
<meta name="author" content="Neil Brommer">
|
|
||||||
<link rel="icon" href="/favicon.ico">
|
|
||||||
|
|
||||||
<!-- theme colors -->
|
|
||||||
<!-- Chrome, Firefox OS and Opera -->
|
|
||||||
<meta name="theme-color" content="#3f51b5">
|
|
||||||
<!-- Windows Phone -->
|
|
||||||
<meta name="msapplication-navbutton-color" content="#3f51b5">
|
|
||||||
<!-- iOS -->
|
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
||||||
|
|
||||||
<!-- Bootstrap -->
|
|
||||||
<link href="/lib/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
|
|
||||||
|
|
||||||
<!-- font awesome -->
|
|
||||||
<script defer src="/lib/fontawesome/svg-with-js/js/fa-brands.js"></script>
|
|
||||||
<script defer src="/lib/fontawesome/svg-with-js/js/fa-regular.js"></script>
|
|
||||||
<script defer src="/lib/fontawesome/svg-with-js/js/fa-solid.js"></script>
|
|
||||||
<script src="/lib/fontawesome/svg-with-js/js/fontawesome.js"></script>
|
|
||||||
|
|
||||||
<!-- Custom styles for this site -->
|
|
||||||
<link href="/css/main.css" rel="stylesheet">
|
|
||||||
|
|
||||||
<!-- Bootstrap core JavaScript -->
|
|
||||||
<script src="/lib/jquery/jquery-3.2.1.js"></script>
|
|
||||||
<script src="/lib/popper/popper.js"></script>
|
|
||||||
<script src="/lib/bootstrap/js/bootstrap.min.js"></script>
|
|
||||||
|
|
||||||
<!-- Custom JS -->
|
|
||||||
<script src="/js/main.js"></script>
|
|
||||||
|
|
||||||
{{ template "_internal/google_analytics_async.html" . }}
|
|
|
@ -1,11 +0,0 @@
|
||||||
<div class="card">
|
|
||||||
<h4 class="card-header">{{ .title }}</h4>
|
|
||||||
<div class="card-body">
|
|
||||||
<p><a class="card-link" href="{{ .URL }}" target="_blank">Link</a></p>
|
|
||||||
|
|
||||||
{{ if isset . "description" }}
|
|
||||||
<p>{{ .description | markdownify }}</p>
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,47 +0,0 @@
|
||||||
<nav id="topnav" class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
|
|
||||||
<div class="container">
|
|
||||||
<a class="navbar-brand" href="/">{{ .Site.Title }}</a>
|
|
||||||
<button class="navbar-toggler mr-0 ml-auto" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse"
|
|
||||||
aria-expanded="false" aria-label="Toggle navigation">
|
|
||||||
<span class="navbar-toggler-icon"></span>
|
|
||||||
</button>
|
|
||||||
<div class="collapse navbar-collapse" id="navbarCollapse">
|
|
||||||
<ul class="navbar-nav mr-auto">
|
|
||||||
|
|
||||||
{{ $currentPage := . }}
|
|
||||||
{{ range .Site.Menus.main }}
|
|
||||||
<li class="nav-item {{if or ($currentPage.IsMenuCurrent "main" .) ($currentPage.HasMenuCurrent "main" .) }} active{{end}}">
|
|
||||||
<a class="nav-link" href="{{ .URL }}">{{.Title}}</a>
|
|
||||||
</li>
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<ul id="social-menu" class="navbar-nav">
|
|
||||||
{{ with .Site.Data.social }}
|
|
||||||
{{ with .github }}
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="{{ . }}" target="_blank" title="GitHub">
|
|
||||||
<span class="fab fa-github-alt"></span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{{ end }}
|
|
||||||
{{ with .linkedin }}
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="{{ . }}" target="_blank" title="LinkedIn">
|
|
||||||
<span class="fab fa-linkedin"></span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{{ end }}
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
<li class="nav-item ml-auto">
|
|
||||||
<button id="btnTheme" type="button" class="btn btn-dark" aria-pressed="false" autocomplete="off">
|
|
||||||
<span id="themeText" class="fas fa-moon"></span>
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
|
@ -1,24 +0,0 @@
|
||||||
<div class="card">
|
|
||||||
<h4 class="card-header">{{ .title }}</h4>
|
|
||||||
<div class="card-body">
|
|
||||||
{{ if .incomplete }}
|
|
||||||
<p class="text-danger"><small>Incomplete</small></p>
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{ if isset . "description" }}
|
|
||||||
<p>{{ .description | markdownify }}</p>
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{ if isset . "links" }}
|
|
||||||
{{ range .links }}
|
|
||||||
<a class="card-link" href="{{ .URL }}"
|
|
||||||
{{ if and (isset . "external") (eq .external true) }}
|
|
||||||
target="_blank"
|
|
||||||
{{ end }}
|
|
||||||
>
|
|
||||||
{{ .title }}
|
|
||||||
</a>
|
|
||||||
{{ end }}
|
|
||||||
{{ end }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,9 +0,0 @@
|
||||||
{{ with $.Site.Data.links }}
|
|
||||||
|
|
||||||
<div class="card-columns">
|
|
||||||
{{ range .link }}
|
|
||||||
{{ partial "linkCard" . }}
|
|
||||||
{{ end }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{ end }}
|
|
4232
package-lock.json
generated
Normal file
4232
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
25
package.json
Normal file
25
package.json
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"name": "websiterewrite",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"watch": "npx @11ty/eleventy --serve",
|
||||||
|
"start": "open http://localhost:8080 & npm run watch",
|
||||||
|
"build": "npx @11ty/eleventy"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"@11ty/eleventy": "^2.0.0",
|
||||||
|
"@11ty/eleventy-navigation": "^0.3.5",
|
||||||
|
"eleventy-sass": "^2.2.3",
|
||||||
|
"markdown-it-deflist": "^2.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-free": "^6.4.0",
|
||||||
|
"bootstrap-icons": "^1.10.5",
|
||||||
|
"normalize.css": "^8.0.1"
|
||||||
|
}
|
||||||
|
}
|
1
src/_data/layout.js
Normal file
1
src/_data/layout.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
module.exports = "../_includes/layouts/layout.njk";
|
46
src/_data/projects.json
Normal file
46
src/_data/projects.json
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Start",
|
||||||
|
"description": "A new tab page that displays lists of links using the browser’s indexedDB to store all data",
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"title": "Site",
|
||||||
|
"url": "https://start.neilbrommer.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Source Code",
|
||||||
|
"url": "https://github.com/NeilBrommer/NewTabPage"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Blazor Start",
|
||||||
|
"description": "A work in progress rewrite of the Start project using Blazor WebAssembly",
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"title": "Source Code",
|
||||||
|
"url": "https://github.com/NeilBrommer/BlazorStart"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Website",
|
||||||
|
"description": "The source code for this website",
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"title": "Source Code",
|
||||||
|
"url": "https://github.com/NeilBrommer/Personal-Site"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Auto Dark",
|
||||||
|
"description": "A small utility for setting/toggling the Windows 10 dark theme. Useful in combination with the Windows Task Scheduler to automatically change the theme.",
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"title": "Source Code",
|
||||||
|
"url": "https://github.com/NeilBrommer/WindowsAutoDark"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
17
src/_data/socialLinks.json
Normal file
17
src/_data/socialLinks.json
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "GitHub",
|
||||||
|
"icon": "github",
|
||||||
|
"url": "https://github.com/NeilBrommer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "CodePen",
|
||||||
|
"icon": "codepen",
|
||||||
|
"url": "https://codepen.io/NeilBrommer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LinkedIn",
|
||||||
|
"icon": "linkedin",
|
||||||
|
"url": "https://www.linkedin.com/in/neilbrommer/"
|
||||||
|
}
|
||||||
|
]
|
87
src/_includes/layouts/layout.njk
Normal file
87
src/_includes/layouts/layout.njk
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en-US">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="author" content="Neil Brommer">
|
||||||
|
<meta name="description" content="The personal website of Neil Brommer">
|
||||||
|
|
||||||
|
<title>
|
||||||
|
{% if (title is defined) and (page.url != "/") %}
|
||||||
|
{{ title }} - Neil Brommer
|
||||||
|
{% else %}
|
||||||
|
Neil Brommer
|
||||||
|
{% endif %}
|
||||||
|
</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="{{ '/css/site.css' | url }}">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<nav class="navbar" aria-label="Main Menu">
|
||||||
|
<ul class="siteNav">
|
||||||
|
{% for section in collections.MainPage | filterDrafts | eleventyNavigation %}
|
||||||
|
<li {% if section.url == page.url %}class="active"{% endif %}>
|
||||||
|
<a href="/#{{ section.title | slugify }}">
|
||||||
|
{% if section.icon is defined %}
|
||||||
|
<svg class="bi" fill="currentColor" role="img">
|
||||||
|
<use xlink:href="/images/fontawesome/solid.svg#{{ section.icon }}" />
|
||||||
|
</svg>
|
||||||
|
{% endif %}
|
||||||
|
{{ section.title }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% set standalonePages = collections.all | filterDrafts | IsNotMainPageSection | eleventyNavigation %}
|
||||||
|
|
||||||
|
{% if standalonePages | length %}
|
||||||
|
<li><hr /></li>
|
||||||
|
|
||||||
|
{% for standalonePage in standalonePages %}
|
||||||
|
<li {% if standalonePage.url == page.url %}class="active"{% endif %}>
|
||||||
|
<a href="{{ standalonePage.url }}">
|
||||||
|
{% if standalonePage.icon is defined %}
|
||||||
|
<svg class="bi" fill="currentColor" role="img">
|
||||||
|
<use xlink:href="/images/fontawesome/solid.svg#{{ standalonePage.icon }}" />
|
||||||
|
</svg>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{{ standalonePage.title }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<ul class="externalNav">
|
||||||
|
{% for externalLink in socialLinks %}
|
||||||
|
<li>
|
||||||
|
<a href="{{ externalLink.url }}" title="{{ externalLink.name }}">
|
||||||
|
<svg class="bi" fill="currentColor" role="img">
|
||||||
|
<use xlink:href="/images/fontawesome/brands.svg#{{ externalLink.icon }}" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<h1 id="{{ title | slugify }}">
|
||||||
|
{{ title }}
|
||||||
|
|
||||||
|
<button class="sidebar-toggle" onclick="setSidebar()">
|
||||||
|
<svg class="bi" fill="currentColor" role="img" aria-label="Toggle Main Menu">
|
||||||
|
<use xlink:href="/images/bootstrap-icons.svg#layout-sidebar-inset" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
{{ content | safe }}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="/js/site.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
30
src/_sections/Colors.njk
Normal file
30
src/_sections/Colors.njk
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
---
|
||||||
|
title: Colors
|
||||||
|
eleventyNavigation:
|
||||||
|
key: Colors
|
||||||
|
icon: eye-dropper
|
||||||
|
order: 3
|
||||||
|
tags: [ "MainPage" ]
|
||||||
|
sectionOrder: 3
|
||||||
|
draft: true
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="color-blocks">
|
||||||
|
<div class="color-block" style="background-color: var(--primary-dark-color); color: white;">
|
||||||
|
Primary Dark
|
||||||
|
</div>
|
||||||
|
<div class="color-block" style="background-color: var(--primary-color); color: white;">
|
||||||
|
Primary
|
||||||
|
</div>
|
||||||
|
<div class="color-block" style="background-color: var(--primary-light-color); color: black;">
|
||||||
|
Primary Light
|
||||||
|
</div>
|
||||||
|
<div class="color-block" style="background-color: var(--complementary-color); color: black">
|
||||||
|
Complementary
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<input type="checkbox" id="testCheckBox" name="testCheckBox" checked />
|
||||||
|
<label for="testCheckBox">This checkbox has the accent color</label>
|
||||||
|
</div>
|
11
src/_sections/Contact.md
Normal file
11
src/_sections/Contact.md
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
---
|
||||||
|
title: Contact
|
||||||
|
eleventyNavigation:
|
||||||
|
key: Contact
|
||||||
|
icon: envelope
|
||||||
|
order: 2
|
||||||
|
tags: [ "MainPage" ]
|
||||||
|
sectionOrder: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
Send an email to `contact @ this site`
|
25
src/_sections/Projects.njk
Normal file
25
src/_sections/Projects.njk
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
title: Projects
|
||||||
|
eleventyNavigation:
|
||||||
|
key: Projects
|
||||||
|
icon: screwdriver-wrench
|
||||||
|
order: 1
|
||||||
|
tags: [ "MainPage" ]
|
||||||
|
sectionOrder: 1
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="row-lg-3">
|
||||||
|
{% for project in projects %}
|
||||||
|
<section class="col card">
|
||||||
|
<h3>{{ project.name }}</h3>
|
||||||
|
<p>{{ project.description }}</p>
|
||||||
|
<ul class="card-links">
|
||||||
|
{% for link in project.links %}
|
||||||
|
<li>
|
||||||
|
<a href="{{ link.url }}" target="_blank">{{ link.title }}</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
3
src/_sections/_sections.11tydata.json
Normal file
3
src/_sections/_sections.11tydata.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"permalink": false
|
||||||
|
}
|
7
src/_sections/main.md
Normal file
7
src/_sections/main.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
title: Neil Brommer
|
||||||
|
sectionOrder: 0
|
||||||
|
tags: [ "MainPage" ]
|
||||||
|
---
|
||||||
|
|
||||||
|
Full-stack web developer at [Washington State University](https://wsu.edu)
|
71
src/css/Components/_base.scss
Normal file
71
src/css/Components/_base.scss
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
@use '_variables';
|
||||||
|
|
||||||
|
*,
|
||||||
|
::before,
|
||||||
|
::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
scroll-behavior: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto,
|
||||||
|
"Helvetica Neue", sans-serif;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--text-color);
|
||||||
|
background-color: var(--background-color);
|
||||||
|
accent-color: var(--primary-color);
|
||||||
|
|
||||||
|
@media (min-width: #{variables.$sidebar-breakpoint}) {
|
||||||
|
display: flex;
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
height: 100dvh;
|
||||||
|
flex: 0 0 var(--sidebar-width);
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 70em;
|
||||||
|
padding: 2rem;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--primary-color);
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-thickness: 2px;
|
||||||
|
text-underline-offset: 0.25em;
|
||||||
|
text-decoration-color: transparent;
|
||||||
|
transition: text-decoration-color 200ms ease;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:active {
|
||||||
|
text-decoration-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-contrast: more) {
|
||||||
|
color: var(--primary-color-dark);
|
||||||
|
text-decoration-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
text-decoration-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border: none;
|
||||||
|
height: 1px;
|
||||||
|
background-color: var(--primary-border-color);
|
||||||
|
}
|
5
src/css/Components/_bootstrap-icons.scss
Normal file
5
src/css/Components/_bootstrap-icons.scss
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
// this allows changing the size using font-size
|
||||||
|
.bi {
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
}
|
46
src/css/Components/_card.scss
Normal file
46
src/css/Components/_card.scss
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
@use '_variables';
|
||||||
|
|
||||||
|
.card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 1rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
border: solid 1px var(--primary-border-color);
|
||||||
|
border-radius: var(--main-border-radius);
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-links {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5em;
|
||||||
|
margin: auto -1rem -1rem -1rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-top: solid 1px var(--primary-border-color);
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
li a {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: var(--main-border-radius);
|
||||||
|
transition: background-color 200ms ease;
|
||||||
|
|
||||||
|
&:hover, &:focus {
|
||||||
|
background-color: var(--nav-hover-background);
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-contrast: more),
|
||||||
|
(prefers-reduced-motion: reduce) {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
src/css/Components/_code.scss
Normal file
19
src/css/Components/_code.scss
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
@use 'variables';
|
||||||
|
|
||||||
|
$code-background-color: darken(variables.$background-color, 5%);
|
||||||
|
$code-background-color-dark: lighten(variables.$background-color-dark, 5%);
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--code-background: #{$code-background-color};
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
--code-background: #{$code-background-color-dark};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background: var(--code-background);
|
||||||
|
padding: 0.125em 0.25em;
|
||||||
|
border: solid 1px var(--primary-border-color);
|
||||||
|
border-radius: calc(var(--main-border-radius) / 2);
|
||||||
|
}
|
22
src/css/Components/_color-block.scss
Normal file
22
src/css/Components/_color-block.scss
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
@use '_variables';
|
||||||
|
|
||||||
|
.color-blocks {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-block {
|
||||||
|
flex: 1 1 0;
|
||||||
|
height: 100px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
border: solid 1px var(--primary-border-color);
|
||||||
|
border-radius: var(--main-border-radius);
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
61
src/css/Components/_columns.scss
Normal file
61
src/css/Components/_columns.scss
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
@use '_variables';
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--column-spacing: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: var(--column-spacing);
|
||||||
|
|
||||||
|
.col {
|
||||||
|
flex: 0 0 100%;
|
||||||
|
|
||||||
|
// Default to md
|
||||||
|
@media (min-width: #{variables.$size-md}) {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin row($name, $breakpoint) {
|
||||||
|
.row-#{$name} {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: var(--column-spacing);
|
||||||
|
|
||||||
|
.col {
|
||||||
|
flex: 0 0 100%;
|
||||||
|
|
||||||
|
@media (min-width: #{$breakpoint}) {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@for $i from 1 through 12 {
|
||||||
|
.row-#{$name}-#{$i} {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: var(--column-spacing);
|
||||||
|
|
||||||
|
.col {
|
||||||
|
flex: 0 0 100%;
|
||||||
|
|
||||||
|
@media (min-width: #{$breakpoint}) {
|
||||||
|
// Even width filling the whole row accounting for gap
|
||||||
|
flex: 0 0 calc((100% / #{$i}) - (var(--column-spacing) / #{$i} * (#{$i} - 1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include row("xs", #{variables.$size-xs});
|
||||||
|
@include row("sm", #{variables.$size-sm});
|
||||||
|
@include row("md", #{variables.$size-md});
|
||||||
|
@include row("lg", #{variables.$size-lg});
|
||||||
|
@include row("xl", #{variables.$size-xl});
|
||||||
|
@include row("2x", #{variables.$size-2x});
|
8
src/css/Components/_headings.scss
Normal file
8
src/css/Components/_headings.scss
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
font-weight: 500;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 3em;
|
||||||
|
}
|
18
src/css/Components/_lists.scss
Normal file
18
src/css/Components/_lists.scss
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
ul {
|
||||||
|
padding-inline-start: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin: 0.25em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dl {
|
||||||
|
dt {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd {
|
||||||
|
padding: 0.25em 0;
|
||||||
|
margin-inline-start: 1em;
|
||||||
|
}
|
||||||
|
}
|
43
src/css/Components/_mobile-header.scss
Normal file
43
src/css/Components/_mobile-header.scss
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
@use 'variables';
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-toggle {
|
||||||
|
font-size: 2rem;
|
||||||
|
|
||||||
|
appearance: none;
|
||||||
|
cursor: pointer;
|
||||||
|
color: inherit;
|
||||||
|
background-color: var(--background-color);
|
||||||
|
border: solid 1px var(--primary-border-color);
|
||||||
|
border-radius: var(--main-border-radius);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.25em;
|
||||||
|
margin: 0.25em;
|
||||||
|
|
||||||
|
transition: background-color 200ms ease, color 200ms ease;
|
||||||
|
|
||||||
|
&:hover, &focus {
|
||||||
|
color: var(--primary-color);
|
||||||
|
background-color: var(--nav-hover-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: var(--primary-light-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: #{variables.$sidebar-breakpoint}) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
117
src/css/Components/_navbar.scss
Normal file
117
src/css/Components/_navbar.scss
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
@use 'sass:color';
|
||||||
|
@use '_variables';
|
||||||
|
|
||||||
|
header {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: calc((var(--sidebar-width) + 2rem) * -1);
|
||||||
|
height: calc(100dvh - 2rem); // Account for margin
|
||||||
|
margin: 1rem;
|
||||||
|
|
||||||
|
border: solid 1px var(--primary-border-color);
|
||||||
|
border-radius: var(--main-border-radius);
|
||||||
|
background-color: rgba(var(--background-color-components), 0.5);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
|
||||||
|
// Goes slightly past the end, then bounces back to the final position
|
||||||
|
transition: left 250ms cubic-bezier(.44,1.36,.74,.97);
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-contrast: more) {
|
||||||
|
backdrop-filter: none;
|
||||||
|
background-color: var(--background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&[aria-hidden="false"] {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: #{variables.$sidebar-breakpoint}) {
|
||||||
|
// Always show the sidebar on larger screens
|
||||||
|
position: sticky;
|
||||||
|
left: unset;
|
||||||
|
height: 100dvh;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
border: none;
|
||||||
|
background-color: unset;
|
||||||
|
backdrop-filter: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.navbar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
width: var(--sidebar-width);
|
||||||
|
height: 100%;
|
||||||
|
padding: 1rem;
|
||||||
|
overflow: scroll;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
display: flex;
|
||||||
|
list-style: none;
|
||||||
|
padding-left: 0;
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.siteNav {
|
||||||
|
flex-direction: column;
|
||||||
|
justify-items: flex-start;
|
||||||
|
gap: 0.5em;
|
||||||
|
|
||||||
|
li {
|
||||||
|
a {
|
||||||
|
// Use flex to correct icon alignment
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
column-gap: 0.5em;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.75em;
|
||||||
|
color: var(--nav-link-color);
|
||||||
|
border-radius: var(--main-border-radius);
|
||||||
|
|
||||||
|
transition: background-color 200ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active a {
|
||||||
|
background-color: var(--nav-active-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(.active) a:hover, &:not(.active) a:focus {
|
||||||
|
background-color: var(--nav-hover-background);
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.externalNav {
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.5em;
|
||||||
|
|
||||||
|
li {
|
||||||
|
a {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0 0.75em;
|
||||||
|
color: var(--nav-link-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
95
src/css/Components/_variables.scss
Normal file
95
src/css/Components/_variables.scss
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
@use 'sass:color';
|
||||||
|
|
||||||
|
@function select-foreground($backgroundColor, $lightColor: white, $darkColor: black) {
|
||||||
|
@if (lightness($backgroundColor) > 60) {
|
||||||
|
@return $darkColor;
|
||||||
|
} @else {
|
||||||
|
@return $lightColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$primary-r: 77;
|
||||||
|
$primary-g: 94;
|
||||||
|
$primary-b: 193;
|
||||||
|
$primary-color: rgb($primary-r, $primary-g, $primary-b);
|
||||||
|
$primary-light-color: lighten($primary-color, 35%);
|
||||||
|
$primary-dark-color: darken($primary-color, 10%);
|
||||||
|
$complementary-color: color.complement($primary-color);
|
||||||
|
|
||||||
|
$text-color: #3b4351;
|
||||||
|
$text-color-dark: white;
|
||||||
|
$background-color-components: 255, 255, 255;
|
||||||
|
$background-color: white;
|
||||||
|
$background-color-dark-components: 33, 33, 36;
|
||||||
|
$background-color-dark: rgb(33, 33, 36);
|
||||||
|
|
||||||
|
$main-border-radius: 8px;
|
||||||
|
|
||||||
|
$nav-active-background: rgba($primary-color, 0.15);
|
||||||
|
$nav-active-background-dark: rgba($primary-color, 0.35);
|
||||||
|
$nav-background-hover-color: rgba($primary-light-color, 0.2);
|
||||||
|
$nav-background-hover-color-dark: rgba($primary-color, 0.2);
|
||||||
|
$nav-link-color: $text-color;
|
||||||
|
$nav-link-color-dark: $text-color-dark;
|
||||||
|
|
||||||
|
$primary-border-color: darken($background-color, 15%);
|
||||||
|
$primary-border-color-dark: lighten($background-color-dark, 15%);
|
||||||
|
$primary-border-color-contrast: darken($background-color, 50%);
|
||||||
|
$primary-border-color-dark-contrast: lighten($background-color-dark, 50%);
|
||||||
|
|
||||||
|
$size-xs: 480px;
|
||||||
|
$size-sm: 600px;
|
||||||
|
$size-md: 840px;
|
||||||
|
$size-lg: 960px;
|
||||||
|
$size-xl: 1280px;
|
||||||
|
$size-2x: 1440px;
|
||||||
|
|
||||||
|
$sidebar-width: 15rem;
|
||||||
|
$sidebar-breakpoint: $size-md;
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--size-xs: 480px;
|
||||||
|
--size-sm: 600px;
|
||||||
|
--size-md: 840px;
|
||||||
|
--size-lg: 960px;
|
||||||
|
--size-xl: 1280px;
|
||||||
|
--size-2x: 1440px;
|
||||||
|
|
||||||
|
--sidebar-width: #{$sidebar-width};
|
||||||
|
--sidebar-breakpoint: #{$sidebar-breakpoint};
|
||||||
|
--main-border-radius: #{$main-border-radius};
|
||||||
|
|
||||||
|
--primary-color: #{$primary-color};
|
||||||
|
--primary-light-color: #{$primary-light-color};
|
||||||
|
--primary-dark-color: #{$primary-dark-color};
|
||||||
|
--complementary-color: #{$complementary-color};
|
||||||
|
|
||||||
|
--primary-border-color: #{$primary-border-color};
|
||||||
|
|
||||||
|
--text-color: #{$text-color};
|
||||||
|
--background-color: #{$background-color};
|
||||||
|
--background-color-components: #{$background-color-components};
|
||||||
|
|
||||||
|
--nav-link-color: #{$nav-link-color};
|
||||||
|
--nav-active-background: #{$nav-active-background};
|
||||||
|
--nav-hover-background: #{$nav-background-hover-color};
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
--text-color: #{$text-color-dark};
|
||||||
|
--background-color: #{$background-color-dark};
|
||||||
|
--background-color-components: #{$background-color-dark-components};
|
||||||
|
--primary-border-color: #{$primary-border-color-dark};
|
||||||
|
|
||||||
|
--nav-link-color: #{$nav-link-color-dark};
|
||||||
|
--nav-active-background: #{$nav-active-background-dark};
|
||||||
|
--nav-hover-background: #{$nav-background-hover-color-dark};
|
||||||
|
|
||||||
|
@media (prefers-contrast: more) {
|
||||||
|
--primary-border-color: #{$primary-border-color-dark-contrast};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-contrast: more) {
|
||||||
|
--primary-border-color: #{$primary-border-color-contrast};
|
||||||
|
}
|
||||||
|
}
|
13
src/css/site.scss
Normal file
13
src/css/site.scss
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
@use 'normalize.css/normalize';
|
||||||
|
|
||||||
|
@use 'Components/_variables';
|
||||||
|
@use 'Components/_base';
|
||||||
|
@use 'Components/_bootstrap-icons';
|
||||||
|
@use 'Components/_columns';
|
||||||
|
@use 'Components/_navbar';
|
||||||
|
@use 'Components/_headings';
|
||||||
|
@use 'Components/_card';
|
||||||
|
@use 'Components/_color-block';
|
||||||
|
@use 'Components/_mobile-header';
|
||||||
|
@use 'Components/_lists';
|
||||||
|
@use 'Components/_code';
|
18
src/index.njk
Normal file
18
src/index.njk
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
---
|
||||||
|
title: Neil Brommer
|
||||||
|
eleventyNavigation:
|
||||||
|
key: Neil Brommer
|
||||||
|
icon: house
|
||||||
|
order: 0
|
||||||
|
tags: [ "MainPage" ]
|
||||||
|
---
|
||||||
|
|
||||||
|
{% for section in collections.MainPage | filterDrafts | IsNotPage(page.url) | IsMainPageSection | orderBySectionOrder %}
|
||||||
|
{% if not loop.first %}
|
||||||
|
<h2 id="{{ section.data.title | slugify }}">{{ section.data.title }}</h2>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<section>
|
||||||
|
{{ section.templateContent | safe }}
|
||||||
|
</section>
|
||||||
|
{% endfor %}
|
48
src/js/site.js
Normal file
48
src/js/site.js
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function dismissSidebarOnClick(event) {
|
||||||
|
if (event.target.closest("header") == null && event.target.closest(".sidebar-toggle") == null) {
|
||||||
|
event.stopPropagation();
|
||||||
|
setSidebar(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSidebar(isOpen) {
|
||||||
|
let header = document.querySelector("header");
|
||||||
|
|
||||||
|
// If isOpen isn't provided, then just toggle the sidebar
|
||||||
|
if (isOpen == null) {
|
||||||
|
let currentlyOpen = header.getAttribute("aria-hidden") == "false";
|
||||||
|
isOpen = !currentlyOpen;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.setAttribute("aria-hidden", (!isOpen).toString());
|
||||||
|
|
||||||
|
if (isOpen) {
|
||||||
|
document.querySelector("body")
|
||||||
|
.addEventListener("click", dismissSidebarOnClick);
|
||||||
|
} else {
|
||||||
|
document.querySelector("body")
|
||||||
|
.removeEventListener("click", dismissSidebarOnClick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make main page sections active on scroll
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const observer = new IntersectionObserver(entries => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
const id = entry.target.previousElementSibling.id;
|
||||||
|
const menuListItem = document.querySelector(`nav li a[href="/#${id}"]`).parentElement;
|
||||||
|
|
||||||
|
if (entry.intersectionRatio > 0) {
|
||||||
|
menuListItem.classList.add("active");
|
||||||
|
} else {
|
||||||
|
menuListItem.classList.remove("active");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll("h1[id] + section, h2[id] + section, h3[id] + section, h4[id] + section, h5[id] + section, h6[id] + section")
|
||||||
|
.forEach(section => observer.observe(section));
|
||||||
|
});
|
48
src/resume.md
Normal file
48
src/resume.md
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
---
|
||||||
|
title: Resume
|
||||||
|
eleventyNavigation:
|
||||||
|
key: Resume
|
||||||
|
icon: file-lines
|
||||||
|
order: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
## Work History
|
||||||
|
|
||||||
|
[Washington State University](https://wsu.edu) [Division of Student Affairs](https://studentaffairs.wsu.edu/)
|
||||||
|
|
||||||
|
: Application Developer, 2018 - present
|
||||||
|
: Created and maintained custom web applications either as standalone software or integrating with
|
||||||
|
third party backends
|
||||||
|
|
||||||
|
|
||||||
|
## Education
|
||||||
|
|
||||||
|
[Eastern Washington University](https://www.ewu.edu/)
|
||||||
|
|
||||||
|
: Bachelor of Science in Computer Science
|
||||||
|
: 2014 - 2018
|
||||||
|
|
||||||
|
[Spokane Community College](https://scc.spokane.edu/)
|
||||||
|
|
||||||
|
: Associate of Applied Science in Network Design and Administration
|
||||||
|
: 2011 - 2013
|
||||||
|
|
||||||
|
## Skills
|
||||||
|
|
||||||
|
* C# - .NET Framework and .NET 5+
|
||||||
|
* [ASP.NET and ASP.NET Core](https://dotnet.microsoft.com/en-us/apps/aspnet) - [WebForms](https://learn.microsoft.com/en-us/aspnet/web-forms/what-is-web-forms), [MVC](https://dotnet.microsoft.com/en-us/apps/aspnet/mvc), [Razor Pages](https://learn.microsoft.com/en-us/aspnet/core/razor-pages/?view=aspnetcore-7.0&tabs=visual-studio), and [WebAPI](https://learn.microsoft.com/en-us/aspnet/core/web-api/)
|
||||||
|
* Entity Framework and [Entity Framework Core](https://learn.microsoft.com/en-us/ef/core/)
|
||||||
|
* [Blazor WebAssembly](https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor)
|
||||||
|
* WinForms and WPF
|
||||||
|
* Xamarin native for MacOS
|
||||||
|
* Version control
|
||||||
|
* Git
|
||||||
|
* [Team Foundation Server/Azure DevOps Server](https://azure.microsoft.com/en-us/products/devops/server)
|
||||||
|
* Web development
|
||||||
|
* HTML, CSS, JavaScript, [SASS](https://sass-lang.com/), [TypeScript](https://www.typescriptlang.org/)
|
||||||
|
* [React](https://react.dev/)
|
||||||
|
* Create and consume REST APIs with [OpenAPI](https://www.openapis.org/), [Swashbuckle](https://github.com/domaindrivendev/Swashbuckle.AspNetCore), and [NSwag](https://github.com/RicoSuter/NSwag)
|
||||||
|
* [WCF](https://learn.microsoft.com/en-us/dotnet/framework/wcf/whats-wcf)/SOAP
|
||||||
|
* Web server administration
|
||||||
|
* Linux (NGINX, Apache, Caddy) and Windows Server (IIS)
|
||||||
|
* Continuous Integration and Continuous Deployment via [Azure DevOps Server](https://azure.microsoft.com/en-us/products/devops/server)
|
Binary file not shown.
|
@ -1,113 +0,0 @@
|
||||||
<?php
|
|
||||||
use PHPMailer\PHPMailer\PHPMailer;
|
|
||||||
use PHPMailer\PHPMailer\Exception;
|
|
||||||
|
|
||||||
require 'PHPMailer/src/Exception.php';
|
|
||||||
require 'PHPMailer/src/PHPMailer.php';
|
|
||||||
require 'PHPMailer/src/SMTP.php';
|
|
||||||
|
|
||||||
include_once "info.php";
|
|
||||||
|
|
||||||
if ($_SERVER["REQUEST_METHOD"] == 'POST') {
|
|
||||||
if (isset($_POST["captcha"]) && !empty($_POST["captcha"])) {
|
|
||||||
|
|
||||||
$captcha = $_POST["captcha"];
|
|
||||||
$url = "https://www.google.com/recaptcha/api/siteverify";
|
|
||||||
$data = array(
|
|
||||||
"secret" => $recaptchaSecret,
|
|
||||||
"response" => $captcha,
|
|
||||||
"remoteip" => $_SERVER["REMOTE_ADDR"]
|
|
||||||
);
|
|
||||||
$options = array(
|
|
||||||
"http" => array(
|
|
||||||
"header" => "Content-Type: application/x-www-form-urlencoded\r\n",
|
|
||||||
"method" => "POST",
|
|
||||||
"content" => http_build_query($data)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
$context = stream_context_create($options);
|
|
||||||
$result = file_get_contents($url, false, $context);
|
|
||||||
if ($result === false) {
|
|
||||||
http_response_code(500);
|
|
||||||
die("Error verifying reCAPTCHA");
|
|
||||||
}
|
|
||||||
$result = json_decode($result, true);
|
|
||||||
|
|
||||||
if ($result["success"] == false) {
|
|
||||||
http_response_code(400);
|
|
||||||
die("Could not verify reCAPTCHA");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (!isset($_POST["name"])) {
|
|
||||||
http_response_code(400);
|
|
||||||
die("A name is required");
|
|
||||||
}
|
|
||||||
$name = trim($_POST["name"]);
|
|
||||||
if ($name == '') {
|
|
||||||
http_response_code(400);
|
|
||||||
die("The name cannot be empty");
|
|
||||||
} else if (strtolower($name) == 'anon' || strToLower($name) == 'anonymous') {
|
|
||||||
http_response_code(400);
|
|
||||||
die("Enter a real name");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($_POST["email"])) {
|
|
||||||
http_response_code(400);
|
|
||||||
die("An email address is required");
|
|
||||||
}
|
|
||||||
$email = trim($_POST["email"]);
|
|
||||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
|
||||||
http_response_code(400);
|
|
||||||
die("Invalid email address");
|
|
||||||
}
|
|
||||||
|
|
||||||
$subject = "Message from contact form";
|
|
||||||
if (isset($_POST["subject"]) && trim($_POST["subject"]) != "") {
|
|
||||||
$subject = $subject . ": " . $_POST["subject"];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($_POST["message"])) {
|
|
||||||
http_response_code(400);
|
|
||||||
die("A message is required");
|
|
||||||
}
|
|
||||||
$message = trim($_POST["message"]);
|
|
||||||
if ($message == '') {
|
|
||||||
http_response_code(400);
|
|
||||||
die("A message is required");
|
|
||||||
}
|
|
||||||
$message = $name . ' -- <' . $email . '><hr>' . $message;
|
|
||||||
|
|
||||||
$mail = new PHPMailer(true); // true enables exceptions
|
|
||||||
try {
|
|
||||||
$mail->isSMTP();
|
|
||||||
//$mail->SMTPDebug = 3;
|
|
||||||
$mail->Host = $mailHost;
|
|
||||||
$mail->SMTPAuth = true;
|
|
||||||
$mail->Username = $mailUser;
|
|
||||||
$mail->Password = $mailPass;
|
|
||||||
$mail->SMTPSecure = 'ssl';
|
|
||||||
$mail->Port = $mailPort;
|
|
||||||
|
|
||||||
$mail->setFrom($mailFrom, 'Website Contact Form');
|
|
||||||
$mail->addAddress($mailDest);
|
|
||||||
$mail->addReplyTo($email);
|
|
||||||
|
|
||||||
$mail->isHTML(true);
|
|
||||||
$mail->Subject = $subject;
|
|
||||||
$mail->Body = $message;
|
|
||||||
|
|
||||||
$mail->Send();
|
|
||||||
} catch (Exception $e) {
|
|
||||||
http_response_code(500);
|
|
||||||
echo 'Message could not be sent.';
|
|
||||||
echo 'Mailer Error: ' . $mail->ErrorInfo;
|
|
||||||
}
|
|
||||||
} else { // no recaptcha
|
|
||||||
http_response_code(400);
|
|
||||||
echo "Bad reCAPTCHA";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
http_response_code(404);
|
|
||||||
}
|
|
||||||
?>
|
|
|
@ -1,26 +0,0 @@
|
||||||
body {
|
|
||||||
padding-top: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header {
|
|
||||||
padding: 10vw 2rem;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
|
|
||||||
color: white;
|
|
||||||
text-shadow: 1px 1px 2px gray;
|
|
||||||
|
|
||||||
box-shadow: 0 1px 3px rgba(0,0,0,0.16), 0 1px 3px rgba(0,0,0,0.23);
|
|
||||||
background: linear-gradient(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1)), url('/img/trianglify.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#headerContent {
|
|
||||||
padding-top: 70px; /* account for nav bar */
|
|
||||||
}
|
|
||||||
|
|
||||||
#header a {
|
|
||||||
color: #a10022 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#title {
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
}
|
|
|
@ -1,149 +0,0 @@
|
||||||
:root {
|
|
||||||
--main-color: #3f51b5; /* R:63, G:82, B:181 */
|
|
||||||
--main-dark-color: #002984; /* R:0, G:41, B:132 */
|
|
||||||
--main-light-color: #757de8; /* R:117, G:125, B:232 */
|
|
||||||
--accent-color: #FFC107; /* R:255, G:193, B:7 */
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
padding-top: 70px;
|
|
||||||
padding-bottom: 40px;
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar.fixed-top {
|
|
||||||
padding-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
background-color: #3f51b5 !important;
|
|
||||||
box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
|
|
||||||
border-bottom: #002984 1px solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item {
|
|
||||||
padding-top: 0.5rem;
|
|
||||||
padding-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 768px) {
|
|
||||||
.navbar-nav .nav-item.active {
|
|
||||||
padding-bottom: 0.25rem;
|
|
||||||
border-bottom: white 0.25rem solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
#btnTheme {
|
|
||||||
margin-left: 0.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 767px) {
|
|
||||||
.navbar-nav .nav-item.active {
|
|
||||||
padding-left: 0.75rem;
|
|
||||||
border-left: white 0.25rem solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-nav .nav-item {
|
|
||||||
padding-left: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
#social-menu {
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
|
|
||||||
#social-menu .nav-item:not(:first-child) {
|
|
||||||
padding-left: 2rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar .navbar-brand {
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
padding-left: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card, .jumbotron {
|
|
||||||
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1:not(.card-header):not(#title), h2:not(.card-header), h3:not(.card-header), h4:not(.card-header), h5:not(.card-header), h6:not(.card-header) {
|
|
||||||
margin-top: 30px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
a:not(.nav-link):not(.navbar-brand) {
|
|
||||||
color: #3f51b5;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover:not(.nav-link):not(.navbar-brand) {
|
|
||||||
color: #757de8;
|
|
||||||
}
|
|
||||||
|
|
||||||
li.col-md-6 {
|
|
||||||
padding-left: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cover {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.required {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.invalid-captcha {
|
|
||||||
box-shadow: 0 0 1.5px 1px red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fa {
|
|
||||||
font-size: 0.85rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.full-height {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dark Mode */
|
|
||||||
|
|
||||||
.dark-mode {
|
|
||||||
color: white;
|
|
||||||
background-color: #343a40;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark-mode .jumbotron {
|
|
||||||
background-color: #2b3136;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark-mode a:not(.navbar-brand):not(.nav-link) {
|
|
||||||
color: #757de8 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark-mode a:not(.navbar-brand):not(.nav-link):hover {
|
|
||||||
color: #3f51b5 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark-mode .card {
|
|
||||||
background-color: #343a40;
|
|
||||||
border-color: rgba(255, 255, 255, 0.125);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark-mode .form-control {
|
|
||||||
background-color: rgb(81, 89, 107);
|
|
||||||
border-color: rgb(60, 66, 80);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark-mode .form-control:focus {
|
|
||||||
box-shadow: 0 0 0 .2rem rgba(255,255,255,.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* for transitioning to/from dark mode */
|
|
||||||
.transition {
|
|
||||||
-webkit-transition: color 0.25s ease-in-out, background-color 0.25s ease-in-out, box-shadow 0.25s ease-in-out, border 0.25s ease-in-out;
|
|
||||||
-moz-transition: color 0.25s ease-in-out, background-color 0.25s ease-in-out, box-shadow 0.25s ease-in-out, border 0.25s ease-in-out;
|
|
||||||
-o-transition: color 0.25s ease-in-out, background-color 0.25s ease-in-out, box-shadow 0.25s ease-in-out, border 0.25s ease-in-out;
|
|
||||||
transition: color 0.25s ease-in-out, background-color 0.25s ease-in-out, box-shadow 0.25s ease-in-out, border 0.25s ease-in-out;
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 5.4 KiB |
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 98 KiB |
|
@ -1,65 +0,0 @@
|
||||||
$(document).ready(function() {
|
|
||||||
$("#contactForm").on("submit", sendForm);
|
|
||||||
});
|
|
||||||
|
|
||||||
function sendForm(e) {
|
|
||||||
e.preventDefault(); // prevent the page from refreshing
|
|
||||||
|
|
||||||
$("#contactSubmit").prop("disabled", true);
|
|
||||||
|
|
||||||
if ($("#successAlert")[0].style.display != 'none') {
|
|
||||||
$("#successAlert").slideUp(250);
|
|
||||||
}
|
|
||||||
if ($("#errorAlert")[0].style.display != 'none') {
|
|
||||||
$("#errorAlert").slideUp(250);
|
|
||||||
}
|
|
||||||
|
|
||||||
var captcha = grecaptcha.getResponse();
|
|
||||||
if (captcha.length == 0) {
|
|
||||||
$(".captcha").addClass("invalid-captcha");
|
|
||||||
} else {
|
|
||||||
$(".captcha").removeClass("invalid-captcha");
|
|
||||||
|
|
||||||
var name = $("#nameField").val();
|
|
||||||
var email = $("#emailField").val();
|
|
||||||
var subject = $("#subjectField").val();
|
|
||||||
var message = $("#messageField").val();
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: "../contact.php",
|
|
||||||
type: "POST",
|
|
||||||
data: {
|
|
||||||
"name": name,
|
|
||||||
"email": email,
|
|
||||||
"subject": subject,
|
|
||||||
"message": message,
|
|
||||||
"captcha": captcha
|
|
||||||
},
|
|
||||||
success: messageSuccess,
|
|
||||||
error: messageError,
|
|
||||||
complete: doneSending
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function messageSuccess(result) {
|
|
||||||
$("#successAlert").slideDown(500);
|
|
||||||
}
|
|
||||||
|
|
||||||
function messageError(result) {
|
|
||||||
var alert = $("#errorAlert");
|
|
||||||
alert.empty();
|
|
||||||
$(document.createTextNode("Error: " + result.responseText)).appendTo(alert);
|
|
||||||
alert.slideDown(500);
|
|
||||||
}
|
|
||||||
|
|
||||||
function doneSending() {
|
|
||||||
var html = $("html");
|
|
||||||
var top = html.scrollTop() + $("body").scrollTop() // Get position of the body
|
|
||||||
|
|
||||||
if(top != 0) {
|
|
||||||
$("html,body").animate({scrollTop:0}, '500');
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#contactSubmit").prop("disabled", false);
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
var theme = window.localStorage.getItem("theme");
|
|
||||||
if (theme != null && theme == "true")
|
|
||||||
$("body").addClass("dark-mode");
|
|
||||||
|
|
||||||
$(document).ready(function () {
|
|
||||||
$("#btnTheme").click(function () {
|
|
||||||
if ($("#btnTheme").hasClass("btn-light")) {
|
|
||||||
transitionLight();
|
|
||||||
} else {
|
|
||||||
transitionDark();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var theme = window.localStorage.getItem("theme");
|
|
||||||
if (theme != null && theme == "true")
|
|
||||||
setDark();
|
|
||||||
});
|
|
||||||
|
|
||||||
function checkTheme() {
|
|
||||||
if (theme != null && theme == "true")
|
|
||||||
setDark();
|
|
||||||
}
|
|
||||||
|
|
||||||
function transitionDark() {
|
|
||||||
$(".card").addClass("transition");
|
|
||||||
$(".jumbotron").addClass("transition");
|
|
||||||
$(".form-control").addClass("transition");
|
|
||||||
$("a:not(.navbar-brand):not(.nav-link)").addClass("transition");
|
|
||||||
$("body").addClass("transition dark-mode");
|
|
||||||
|
|
||||||
setTimeout(endTransition, 250);
|
|
||||||
|
|
||||||
$("#btnTheme").removeClass("btn-dark").addClass("transition btn-light");
|
|
||||||
$("#themeText").replaceWith($("<span>").attr("id", "themeText").addClass("fas fa-sun"));
|
|
||||||
|
|
||||||
window.localStorage.setItem("theme", "true");
|
|
||||||
}
|
|
||||||
|
|
||||||
function transitionLight() {
|
|
||||||
$(".card").addClass("transition");
|
|
||||||
$(".jumbotron").addClass("transition");
|
|
||||||
$(".form-control").addClass("transition");
|
|
||||||
$("a:not(.navbar-brand):not(.nav-link)").addClass("transition");
|
|
||||||
$("body").addClass("transition").removeClass("dark-mode");
|
|
||||||
|
|
||||||
setTimeout(endTransition, 250);
|
|
||||||
|
|
||||||
$("#btnTheme").removeClass("btn-light").addClass("transition btn-dark");
|
|
||||||
$("#themeText").replaceWith($("<span>").attr("id", "themeText").addClass("fas fa-moon"));
|
|
||||||
|
|
||||||
window.localStorage.setItem("theme", "false");
|
|
||||||
}
|
|
||||||
|
|
||||||
function endTransition() {
|
|
||||||
$("body").removeClass("transition");
|
|
||||||
$(".card").removeClass("transition");
|
|
||||||
$(".jumbotron").removeClass("transition");
|
|
||||||
$("a:not(.navbar-brand):not(.nav-link)").removeClass("transition");
|
|
||||||
$(".form-control").removeClass("transition");
|
|
||||||
}
|
|
||||||
|
|
||||||
function setDark() {
|
|
||||||
$("body").addClass("dark-mode");
|
|
||||||
|
|
||||||
$("#btnTheme").removeClass("btn-dark").addClass("btn-light");
|
|
||||||
$("#themeText").replaceWith($("<span>").attr("id", "themeText").addClass("fas fa-sun"));
|
|
||||||
}
|
|
Loading…
Reference in a new issue