Initial commit
9
.gitignore
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
_site
|
||||||
|
.sass-cache
|
||||||
|
.jekyll-cache
|
||||||
|
.jekyll-metadata
|
||||||
|
vendor
|
||||||
|
Gemfile.lock
|
||||||
|
.idea
|
||||||
|
/package.json
|
||||||
|
node_modules
|
4
Gemfile
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# frozen_string_literal
|
||||||
|
|
||||||
|
source "https://rubygems.org"
|
||||||
|
gemspec
|
12
LICENSE
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
Copyright © 2023 Owen Ryan
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Bundled software:
|
||||||
|
- Bootstrap (assets/bootstrap/): MIT License
|
49
README.md
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
# Homepage
|
||||||
|
|
||||||
|
This repository contains all the code that I use to host my website. Pages are written in markdown and are rendered to
|
||||||
|
static HTML using Jekyll and a custom Bootstrap theme that I created.
|
||||||
|
|
||||||
|
## Installing dependencies
|
||||||
|
|
||||||
|
Based on the [Jekyll quick start guide](https://jekyllrb.com/docs/)
|
||||||
|
|
||||||
|
**Note:** Make sure Ruby and RubyGems are installed
|
||||||
|
|
||||||
|
1. Install the jekyll and bundler gem packages
|
||||||
|
```shell
|
||||||
|
gem install jekyll bundler
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Install site dependencies
|
||||||
|
|
||||||
|
**Make sure you are in the root directory of the git repository**
|
||||||
|
```shell
|
||||||
|
bundle install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building the static site
|
||||||
|
|
||||||
|
Static HTML and CSS files can be built using the following command.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
bundle exec jekyll build
|
||||||
|
```
|
||||||
|
|
||||||
|
All static files should now be in the ```site/``` directory.
|
||||||
|
|
||||||
|
## Important note:
|
||||||
|
|
||||||
|
The static site's root is ``_site/``. Everything outside of that is processed by Jekyll and placed in `_site/`.
|
||||||
|
|
||||||
|
**Note:** Some of the final code in ``_site`` may not have proper indentation, and some comments might have been
|
||||||
|
removed.
|
||||||
|
|
||||||
|
## Sources I used in a bunch of places
|
||||||
|
|
||||||
|
- [W3Schools](https://www.w3schools.com/)
|
||||||
|
- [MDN](https://developer.mozilla.org/en-US/)
|
||||||
|
- [Bootstrap Docs](https://getbootstrap.com/docs/5.3/getting-started/introduction/)
|
||||||
|
- [Bootstrap Examples](https://getbootstrap.com/docs/5.3/examples/)
|
||||||
|
- [Jekyll Documentation](https://jekyllrb.com/docs/)
|
||||||
|
- [Liquid Documentation](https://shopify.github.io/liquid/)
|
||||||
|
- [SASS Documentation](https://sass-lang.com/guide)
|
43
_config.yml
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
# Welcome to Jekyll!
|
||||||
|
#
|
||||||
|
# This config file is meant for settings that affect your whole blog, values
|
||||||
|
# which you are expected to set up once and rarely edit after that. If you find
|
||||||
|
# yourself editing this file very often, consider using Jekyll's data files
|
||||||
|
# feature for the data you need to update frequently.
|
||||||
|
#
|
||||||
|
# For technical reasons, this file is *NOT* reloaded automatically when you use
|
||||||
|
# 'bundle exec jekyll serve'. If you change this file, please restart the server process.
|
||||||
|
#
|
||||||
|
# If you need help with YAML syntax, here are some quick references for you:
|
||||||
|
# https://learn-the-web.algonquindesign.ca/topics/markdown-yaml-cheat-sheet/#yaml
|
||||||
|
# https://learnxinyminutes.com/docs/yaml/
|
||||||
|
#
|
||||||
|
# Site settings
|
||||||
|
# These are used to personalize your new site. If you look in the HTML files,
|
||||||
|
# you will see them accessed via {{ site.title }}, {{ site.email }}, and so on.
|
||||||
|
# You can create any custom variable you would like, and they will be accessible
|
||||||
|
# in the templates via {{ site.myvariable }}.
|
||||||
|
|
||||||
|
title: Owen Ryan
|
||||||
|
author: Owen Ryan
|
||||||
|
description: >- # this means to ignore newlines until "baseurl:"
|
||||||
|
The official homepage of Owen Ryan!
|
||||||
|
baseurl: "/" # the subpath of your site, e.g. /blog
|
||||||
|
# TODO: Baseurl currently does not do anything.
|
||||||
|
|
||||||
|
# Build settings
|
||||||
|
theme: homepage
|
||||||
|
|
||||||
|
# Site-wide SEO tags
|
||||||
|
SEO_tags: [ "Owen Ryan" ]
|
||||||
|
|
||||||
|
sass:
|
||||||
|
sass_dir: _sass
|
||||||
|
|
||||||
|
exclude:
|
||||||
|
- .idea
|
||||||
|
- README.md
|
||||||
|
- LICENSE
|
||||||
|
- homepage.gemspec
|
||||||
|
- package.json
|
||||||
|
- package-lock.json
|
11
_data/navigation.yml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# Links in the navigation bar
|
||||||
|
|
||||||
|
header:
|
||||||
|
- name: Home
|
||||||
|
link: /
|
||||||
|
- name: About
|
||||||
|
link: /about
|
||||||
|
- name: Projects
|
||||||
|
link: /projects
|
||||||
|
- name: Contact
|
||||||
|
link: /contact
|
26
_data/social-media.yml
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# Links on the landing page
|
||||||
|
|
||||||
|
links:
|
||||||
|
- name: Git
|
||||||
|
url: https://code.owenryan.us/owenryan
|
||||||
|
style:
|
||||||
|
fa-classes: [ fa-brands fa-git-alt ]
|
||||||
|
color-primary: F44D27
|
||||||
|
- name: Mastodon
|
||||||
|
url: https://activitypub.owenryan.us/@owen
|
||||||
|
style:
|
||||||
|
# TODO: Replace with activitypub logo when available (https://github.com/FortAwesome/Font-Awesome/issues/19668)
|
||||||
|
fa-classes: [ fa-brands, fa-mastodon ]
|
||||||
|
color-primary: 6364FF
|
||||||
|
- name: LinkedIn
|
||||||
|
url: https://www.linkedin.com/in/owenryandev/
|
||||||
|
style:
|
||||||
|
fa-classes: [ fa-brands, fa-linkedin ]
|
||||||
|
color-primary: 0077B5
|
||||||
|
- name: LeetCode
|
||||||
|
url: https://leetcode.com/owenryan/
|
||||||
|
style:
|
||||||
|
# Leetcode's logo is not in fontawesome so just using this generic one
|
||||||
|
fa-classes: [ fa-solid, fa-laptop-code ]
|
||||||
|
# Leetcode does not have a press kit, and does not publish their brand colors. Hopefully this is close enough.
|
||||||
|
color-primary: FFA116
|
8
_includes/footer.html
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{% comment %}
|
||||||
|
Based on bootstrap footer examples
|
||||||
|
https://getbootstrap.com/docs/5.3/examples/footers/
|
||||||
|
https://getbootstrap.com/docs/5.3/examples/sticky-footer-navbar/#
|
||||||
|
{% endcomment %}
|
||||||
|
<footer class="row justify-content-center mt-auto py-3 bg-body-tertiary">
|
||||||
|
<span class="col footer-text mx-5">© 2023 Owen Ryan | Icons by FontAwesome</span>
|
||||||
|
</footer>
|
30
_includes/head.html
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
{% comment %}
|
||||||
|
Set meta description to the page description if it has one, otherwise fallback to the site description
|
||||||
|
{% endcomment %}
|
||||||
|
<meta name="description"
|
||||||
|
content="{% if page.description %}{{ page.description }}{% else %}{{ site.description }}{% endif %}">
|
||||||
|
{% comment %}
|
||||||
|
Add page keywords if there are any
|
||||||
|
{% endcomment %}
|
||||||
|
{% assign tags = site.SEO_tags %}
|
||||||
|
{% if page.SEO_tags %}
|
||||||
|
{% assign tags = tags | concat: page.SEO_tags %}
|
||||||
|
{% endif %}
|
||||||
|
<meta name="keywords" content="{{ tags | join: ", " }}">
|
||||||
|
<meta name="author" content="{{ site.author }}">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>{%- if page.title -%}{{ page.title }} | {% endif %}{{ site.title }}</title>
|
||||||
|
<script src="/assets/utils.js"></script>
|
||||||
|
{% comment %}
|
||||||
|
Dependencies are self-hosted instead of using a public CDN.
|
||||||
|
See this article for more info https://httptoolkit.com/blog/public-cdn-risks/
|
||||||
|
{% endcomment %}
|
||||||
|
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link href="/css/style.css" rel="stylesheet">
|
||||||
|
<!-- Load fontawesome here for faster loadtimes: https://stackoverflow.com/a/35880730/9523246 -->
|
||||||
|
<script>
|
||||||
|
lazyLoadCSS('https://use.fontawesome.com/releases/v6.4.0/css/all.css');
|
||||||
|
</script>
|
||||||
|
</head>
|
18
_includes/header.html
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<div class="container">
|
||||||
|
<header class="header d-flex flex-wrap justify-content-center py-3 mb-4 border-bottom">
|
||||||
|
<a class="d-flex align-items-center mb-3 mb-md-0 me-md-auto link-body-emphasis text-decoration-none"
|
||||||
|
href="/">
|
||||||
|
<img id="gravatar" class="bi me-2" alt="Owen's Gravatar"
|
||||||
|
src="https://www.gravatar.com/avatar/6ad4a9bcc388180c6d2ca34e0c185a8c?size=128">
|
||||||
|
<span class="fs-4">Owen Ryan</span>
|
||||||
|
</a>
|
||||||
|
<ul class="nav nav-pills">
|
||||||
|
{%- for link in site.data.navigation.header -%}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link{% if page.url == link.link %} active{% endif %}" href="{{ link.link }}"
|
||||||
|
aria-current="page">{{ link.name }}</a>
|
||||||
|
</li>
|
||||||
|
{%- endfor -%}
|
||||||
|
</ul>
|
||||||
|
</header>
|
||||||
|
</div>
|
12
_includes/social-media-icons.html
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{% comment %}
|
||||||
|
Social media icons are dynamically rendered based on the contents of _data/social-media.yml
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
{%- for icon in site.data.social-media.links -%}
|
||||||
|
<a class="col social-button" href="{{ icon.url }}" target="_blank" rel="noopener noreferrer">
|
||||||
|
<i class="{{ icon.style.fa-classes | join: " " }} p-3" title="{{ icon.name }}"
|
||||||
|
style="color:#{{ icon.style.color-primary }};"></i>
|
||||||
|
</a>
|
||||||
|
{%- endfor -%}
|
||||||
|
</div>
|
12
_layouts/base.html
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
---
|
||||||
|
# Base HTML layout with the bare essentials
|
||||||
|
---
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html class="h-100" lang="en" data-bs-theme="dark">
|
||||||
|
{% include head.html %}
|
||||||
|
<body class="d-flex flex-column h-100">
|
||||||
|
{{ content }}
|
||||||
|
<script src="/assets/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
8
_layouts/basicmd.html
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
# Layout for basic markdown pages. Everything is nested in a container and row
|
||||||
|
layout: main
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
{{ content }}
|
||||||
|
</div>
|
12
_layouts/main.html
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
---
|
||||||
|
# Base layout with header, footer, and content is in a main tag
|
||||||
|
layout: base
|
||||||
|
---
|
||||||
|
|
||||||
|
{% include header.html %}
|
||||||
|
|
||||||
|
<main class="flex-shrink-0">
|
||||||
|
{{ content }}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
{% include footer.html %}
|
33
_layouts/project.html
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
---
|
||||||
|
layout: main
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row d-flex">
|
||||||
|
<div class="col-md-auto p-2">
|
||||||
|
<div class="card project-card">
|
||||||
|
{% if page.thumbnail_url %}
|
||||||
|
<img src="{{ page.thumbnail_url }}" class="card-img-top" alt="{{ page.title }} thumbnail">
|
||||||
|
{% endif %}
|
||||||
|
<div class="card-body">
|
||||||
|
<p>{{ page.description }}</p>
|
||||||
|
{% if page.project_url %}
|
||||||
|
<a href="{{ page.project_url }}" class="btn btn-primary">
|
||||||
|
<i class="fa-solid fa-arrow-up-right-from-square"></i>
|
||||||
|
Project
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if page.source_url %}
|
||||||
|
<a href="{{ page.source_url }}" class="btn btn-primary">
|
||||||
|
<i class="fa-solid fa-code"></i>
|
||||||
|
Source Code
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
{{ content }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
89
_posts/2023-5-6-calculators.md
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
---
|
||||||
|
layout: project
|
||||||
|
title: Adventures with old calculators
|
||||||
|
description: Shenanigans with graphing calculators my school was throwing away
|
||||||
|
thumbnail_url: /assets/images/calculators.webp
|
||||||
|
SEO_tags: [Calculator, Texas Instruments, TI82, Z80, Assembly, MS-DOS, Overclocking]
|
||||||
|
---
|
||||||
|
|
||||||
|
**Note: This article is still expanding, check back later for more content!**
|
||||||
|
|
||||||
|
In January 2023, my high school was getting rid of over a hundred old calculators. Around half were mismatched
|
||||||
|
scientific and 4-function calculators, and the other half consisted of graphing calculators from the early and mid
|
||||||
|
1990s. Most of the graphing calculators are Texas Instruments TI-82, so this article will be focused on them, but I do
|
||||||
|
plan on making other posts.
|
||||||
|
|
||||||
|
After replacing the dead batteries every single TI-82 turned on and functioned, but some had malfunctioning screens.
|
||||||
|
|
||||||
|
# Overclocking
|
||||||
|
|
||||||
|
This section is based on
|
||||||
|
[this archived webpage](https://web.archive.org/web/20220303160216/http://richfiles.solarbotics.net/Turbo82.html).
|
||||||
|
|
||||||
|
There is a 26pf capacitor labeled **C7** on the top right of the TI-82's motherboard. When this is replaced with a 15pf
|
||||||
|
capacitor, the speed of the calculator increases by 2-2.5x.
|
||||||
|
|
||||||
|
Here is the resistor before and after replacement:
|
||||||
|
<img src="/assets/images/resistorcomparison.webp" class="auto-scale" alt="Split photo of motherboard with old and new resistor">
|
||||||
|
|
||||||
|
And here is a speed comparison between a stock TI-82 (left), and an overclocked TI-82 (right):
|
||||||
|
<video src="/assets/videos/calculatorperf.webm" class="auto-scale" loop autoplay></video>
|
||||||
|
|
||||||
|
# Connecting to computer
|
||||||
|
|
||||||
|
The TI-82 and TI-85 introduced the ability to connect to another calculator or to a computer to share programs.
|
||||||
|
|
||||||
|
## Hardware
|
||||||
|
|
||||||
|
The TI-82 (and TI-85) used the TI Graph-link adapter, which connected to the calculator's link port and to a computer's
|
||||||
|
9 or 25-pin serial port. Luckily, this serial adapter can be converted to USB (shown below).
|
||||||
|
|
||||||
|
<img src="/assets/images/graphlinkadapter.webp" class="auto-scale" alt="Photo of TI-Graph Link adapter attached to a serial to USB adapter">
|
||||||
|
|
||||||
|
## Software
|
||||||
|
|
||||||
|
The TI-Graph Link™ software only supports very old versions of Microsoft Windows based on DOS, and some ancient versions
|
||||||
|
of Mac OS. I chose to run the software in an MS-DOS virtual machine with Windows 3.1 since I already have experience
|
||||||
|
with the platform.
|
||||||
|
|
||||||
|
If you want to replicate my virtual machine, use the [MS-DOS 6.22](https://winworldpc.com/product/ms-dos/622) and
|
||||||
|
[Windows 3.1](https://winworldpc.com/product/windows-3/31) images from WinWorldPC, and use this archive of the
|
||||||
|
[TI Graph Link Software](https://archive.org/details/TiGraphLink). For transferring files I decided to use a physical
|
||||||
|
Floppy Drive since I had one lying around. Here is my Proxmox configuration file (I would recommend editing the config
|
||||||
|
file after creating a VM through Proxmox).
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Replace with your floppy drive block device
|
||||||
|
args: -fda /dev/sdb
|
||||||
|
acpi: 0
|
||||||
|
|
||||||
|
# You may need to enable booting from floppy to install DOS
|
||||||
|
boot: order=ide0
|
||||||
|
cores: 1
|
||||||
|
cpu: kvm32
|
||||||
|
hotplug: disk
|
||||||
|
|
||||||
|
# Change this to the storage device you want to store your OS
|
||||||
|
ide0: local-lvm:vm-69420-disk-0,size=2G
|
||||||
|
|
||||||
|
localtime: 0
|
||||||
|
memory: 512
|
||||||
|
name: msdos
|
||||||
|
numa: 0
|
||||||
|
ostype: other
|
||||||
|
scsihw: virtio-scsi-pci
|
||||||
|
|
||||||
|
# Replace with your serial adapter
|
||||||
|
serial0: /dev/ttyUSB0
|
||||||
|
|
||||||
|
sockets: 1
|
||||||
|
tablet: 1
|
||||||
|
tags: ms-dos
|
||||||
|
vga: std
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## What's next
|
||||||
|
|
||||||
|
Both CRASH and ASH allow you to create and load programs written in Z80 assembly. In the near future I plan on creating
|
||||||
|
some basic programs in assembly and stretching the limits on what can be run on a TI-82.
|
36
_posts/2023-5-6-informinator.md
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
---
|
||||||
|
layout: project
|
||||||
|
title: "Informinator"
|
||||||
|
description: News aggregator built with Python, AioHTTP, and Jinja
|
||||||
|
thumbnail_url: /assets/images/informinator.webp
|
||||||
|
project_url: https://informinator.owenryan.us/
|
||||||
|
# TODO: Uncomment this when source code is published
|
||||||
|
# source_url: https://code.owenryan.us/owenryan/informinator
|
||||||
|
SEO_tags: [Python, aiohttp, News]
|
||||||
|
---
|
||||||
|
|
||||||
|
Informinator is the one-stop site for all of your news needs! It aims to be a central hub that delivers the stories you
|
||||||
|
need to know while also funneling you to the proper news sources to get more information. Informinator is currently in
|
||||||
|
a very early beta and only sources news from Al Jazeera, BBC News, The Associated Press, and editorials from The
|
||||||
|
Guardian.
|
||||||
|
|
||||||
|
**Note: Informinator will be open-sourced in the next few weeks when the server is stable**
|
||||||
|
|
||||||
|
## Origins
|
||||||
|
|
||||||
|
During the spring 2023 semester, two separate classes I was taking had weekly news quizzes that checked that you had
|
||||||
|
been following current events. I decided that the best way to study for these quizzes was to create a news aggregator
|
||||||
|
to simplify the process of looking at several news outlets per day.
|
||||||
|
|
||||||
|
## Current issues
|
||||||
|
|
||||||
|
The first version of Informinator was hacked together over a weekend, so some design choices are suboptimal. Some of these include:
|
||||||
|
|
||||||
|
- All articles are loaded on page-load, even though most are hidden behind the carousel.
|
||||||
|
- Informinator's RSS parsing code only supports feeds from [RSSHub](https://docs.rsshub.app/en/).
|
||||||
|
- RSSHub's New York Times English feed is currently unusable due to [a parsing error](https://github.com/DIYgod/RSSHub/issues/12371).
|
||||||
|
- All articles are cached in Python when an in-memory database such as Redis would greatly improve performance.
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
Informinator's public roadmap can be found [here](https://informinator.owenryan.us/roadmap).
|
16
_posts/2023-5-6-jambox.md
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
---
|
||||||
|
layout: project
|
||||||
|
title: "Jambox"
|
||||||
|
description: "Discord music bot built with Hikari and Lavalink"
|
||||||
|
project_url: https://discord.com/api/oauth2/authorize?client_id=1102705272805404722&permissions=4298394880&scope=applications.commands%20bot
|
||||||
|
thumbnail_url: /assets/images/jambox.webp
|
||||||
|
SEO_tags: [Discord, Python, Hikari, Lavalink]
|
||||||
|
---
|
||||||
|
|
||||||
|
Jambox is a *soon-to-be* open source Discord music bot built for convince. It utilises the
|
||||||
|
[Lavalink](https://github.com/lavalink-devs/Lavalink) audio node for streaming music, and also adds support for
|
||||||
|
streaming local files¹. Jambox was built from the ground up using Discord's modern feature set, and
|
||||||
|
utilises [slash commands](https://support.discord.com/hc/en-us/articles/1500000368501-Slash-Commands-FAQ), embeds,
|
||||||
|
message components¹, and modals¹.
|
||||||
|
|
||||||
|
- ¹ - Features are not currently implemented, but are planned additions
|
41
_posts/2023-5-6-website.md
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
---
|
||||||
|
layout: project
|
||||||
|
title: This website
|
||||||
|
description: The self-built site using Jekyll, Bootstrap, TypeScript, and more!
|
||||||
|
thumbnail_url: /assets/images/website.webp
|
||||||
|
project_url: https://owenryan.us/
|
||||||
|
source_url: https://code.owenryan.us/owenryan/owenryan.us
|
||||||
|
SEO_tags: [web, html, css, jekyll]
|
||||||
|
---
|
||||||
|
|
||||||
|
After squatting on this domain for 7 years, I finally stopped procrastinating and decided to create a website.
|
||||||
|
Here is everything I used to turn my visions into reality.
|
||||||
|
|
||||||
|
## Structure
|
||||||
|
|
||||||
|
This website was created using the [Jekyll](https://jekyllrb.com/) static site generator, which runs on the
|
||||||
|
[Liquid](https://shopify.github.io/liquid/) template engine. Jekyll supports writing posts in [Markdown](https://en.wikipedia.org/wiki/Markdown), which makes writing simple pages
|
||||||
|
like this one much easier. On the other hand, using plain markdown makes embedding videos and icons much more difficult.
|
||||||
|
|
||||||
|
## Theme
|
||||||
|
|
||||||
|
Every page is running a custom theme built with the [Bootstrap](https://getbootstrap.com/) CSS library, and styled wit
|
||||||
|
[SASS](https://sass-lang.com/). Jekyll automatically renders markdown files to fit the site theme.
|
||||||
|
|
||||||
|
## Typescript
|
||||||
|
|
||||||
|
When it comes to creating websites, I try to keep scripting down to a minimum to improve client performance. On this
|
||||||
|
site, there are currently only three times Javascript is used on this site.
|
||||||
|
|
||||||
|
- Lazy loading Stylesheets (Used for loading FontAwesome icons in the background)
|
||||||
|
- Gradient rotator (Only on index.html)
|
||||||
|
- "Get Email Address" button in contact.html (Decodes an obfuscated version of my email address to prevent scraping)
|
||||||
|
|
||||||
|
I chose to work with TypeScript over JavaScript since I prefer to use static-typing whenever possible to avoid confusion
|
||||||
|
and to allow better static analysis and linting. On the other hand, Jekyll only has built-in support for CoffeeScript,
|
||||||
|
so compiling to JavaScript has to be done manually.
|
||||||
|
|
||||||
|
## Hosting
|
||||||
|
|
||||||
|
Jekyll produces static HTML and CSS files, meaning that they can be hosted anywhere. I chose to host this website using
|
||||||
|
the [Apache webserver](https://httpd.apache.org/) because it's reliable and has better documentation than NGINX.
|
27
_sass/style.sass
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
// Set the margins and size of social media icons
|
||||||
|
.social-button
|
||||||
|
margin: .5em
|
||||||
|
font-size: 3vw
|
||||||
|
|
||||||
|
// Make the gravatar icon a circle
|
||||||
|
#gravatar
|
||||||
|
border-radius: 50%
|
||||||
|
height: 50px
|
||||||
|
|
||||||
|
// Set the width for project cards to 18rem and set the margin to auto (will center the card in the div)
|
||||||
|
.project-card
|
||||||
|
width: 18rem
|
||||||
|
margin: auto
|
||||||
|
|
||||||
|
// https://stackoverflow.com/a/26056554/14395984
|
||||||
|
.auto-scale
|
||||||
|
width: 100%
|
||||||
|
height: auto
|
||||||
|
max-height: 100%
|
||||||
|
|
||||||
|
// Remove bullet points from the about page
|
||||||
|
// https://stackoverflow.com/questions/1027354/i-need-an-unordered-list-without-any-bullets
|
||||||
|
main li
|
||||||
|
list-style-type: none
|
||||||
|
padding-left: 2em
|
||||||
|
text-indent: -2em
|
49
about.md
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
---
|
||||||
|
layout: basicmd
|
||||||
|
title: About
|
||||||
|
permalink: /about
|
||||||
|
---
|
||||||
|
|
||||||
|
Hey there!
|
||||||
|
|
||||||
|
I'm currently a high school specialising in Computer Science and related fields. I do some occasional side jobs,
|
||||||
|
so [contact me](/contact) if you are interested!
|
||||||
|
|
||||||
|
## Skills
|
||||||
|
|
||||||
|
### Proficient
|
||||||
|
|
||||||
|
- <i class="fa-brands fa-python"></i> Python
|
||||||
|
- <i class="fa-brands fa-discord"></i>
|
||||||
|
[hikari](https://pypi.org/project/hikari/)/[lightbulb](https://pypi.org/project/hikari-lightbulb/)/[miru](https://pypi.org/project/hikari-miru/)
|
||||||
|
Discord bot stack
|
||||||
|
- <i class="fa-solid fa-code-branch"></i> Check out the source code for the [Jambox](/2023/05/06/jambox.html) music bot for a representation of my
|
||||||
|
current abilities
|
||||||
|
- <i class="fa-solid fa-globe"></i> [aiohttp](https://pypi.org/project/aiohttp/) asynchronus web server
|
||||||
|
- <i class="fa-solid fa-layer-group"></i> Experience integrating Jinja templates
|
||||||
|
using [aiohttp-jinja2](https://pypi.org/project/aiohttp-jinja2/)
|
||||||
|
- <i class="fa-solid fa-server"></i> [Proxmox virtual environment](https://www.proxmox.com/en/proxmox-ve)
|
||||||
|
- <i class="fa-solid fa-boxes-stacked"></i> Experience with both Virtual Machines and Linux Containers
|
||||||
|
- <i class="fa-solid fa-desktop"></i> Experience configuring Linux, Windows, and DOS virtual machines
|
||||||
|
- <i class="fa-brands fa-linux"></i> Linux command line (sh/bash/zsh)
|
||||||
|
|
||||||
|
### Emerging
|
||||||
|
|
||||||
|
Skills that I am still working on improving
|
||||||
|
|
||||||
|
- <i class="fa-brands fa-docker"></i> Building Docker containers for Python projects
|
||||||
|
- <i class="fa-solid fa-microchip"></i> C
|
||||||
|
- <i class="fa-solid fa-memory"></i> C++
|
||||||
|
- <i class="fa-solid fa-chart-line"></i> Metrics aggregation using Prometheus and Grafana
|
||||||
|
- <i class="fa-brands fa-square-js"></i> JavaScript/TypeScript
|
||||||
|
- <i class="fa-solid fa-pen-ruler"></i> Frontend programming & Design
|
||||||
|
- <i class="fa-brands fa-html5"></i> HTML
|
||||||
|
- <i class="fa-brands fa-css3-alt"></i> CSS/SASS
|
||||||
|
- <i class="fa-brands fa-bootstrap"></i> Bootstrap
|
||||||
|
- <i class="fa-solid fa-circle-info"></i> I wrote this website and the Informinator news aggregator from scratch, check out their source code if you wnt to
|
||||||
|
see my current abilities
|
||||||
|
|
||||||
|
### Other
|
||||||
|
|
||||||
|
- <i class="fa-solid fa-mountain-sun"></i> Bryce 3.1 (The raytracing renderer from 1997)
|
||||||
|
- <i class="fa-solid fa-circle-info"></i> Used to create the logo for [Jambox](/2023/05/06/jambox.html)
|
4085
assets/bootstrap/css/bootstrap-grid.css
vendored
Normal file
1
assets/bootstrap/css/bootstrap-grid.css.map
Normal file
6
assets/bootstrap/css/bootstrap-grid.min.css
vendored
Normal file
1
assets/bootstrap/css/bootstrap-grid.min.css.map
Normal file
4084
assets/bootstrap/css/bootstrap-grid.rtl.css
vendored
Normal file
1
assets/bootstrap/css/bootstrap-grid.rtl.css.map
Normal file
6
assets/bootstrap/css/bootstrap-grid.rtl.min.css
vendored
Normal file
1
assets/bootstrap/css/bootstrap-grid.rtl.min.css.map
Normal file
591
assets/bootstrap/css/bootstrap-reboot.css
vendored
Normal file
|
@ -0,0 +1,591 @@
|
||||||
|
/*!
|
||||||
|
* Bootstrap Reboot v5.3.0-alpha3 (https://getbootstrap.com/)
|
||||||
|
* Copyright 2011-2023 The Bootstrap Authors
|
||||||
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||||
|
*/
|
||||||
|
:root,
|
||||||
|
[data-bs-theme=light] {
|
||||||
|
--bs-blue: #0d6efd;
|
||||||
|
--bs-indigo: #6610f2;
|
||||||
|
--bs-purple: #6f42c1;
|
||||||
|
--bs-pink: #d63384;
|
||||||
|
--bs-red: #dc3545;
|
||||||
|
--bs-orange: #fd7e14;
|
||||||
|
--bs-yellow: #ffc107;
|
||||||
|
--bs-green: #198754;
|
||||||
|
--bs-teal: #20c997;
|
||||||
|
--bs-cyan: #0dcaf0;
|
||||||
|
--bs-black: #000;
|
||||||
|
--bs-white: #fff;
|
||||||
|
--bs-gray: #6c757d;
|
||||||
|
--bs-gray-dark: #343a40;
|
||||||
|
--bs-gray-100: #f8f9fa;
|
||||||
|
--bs-gray-200: #e9ecef;
|
||||||
|
--bs-gray-300: #dee2e6;
|
||||||
|
--bs-gray-400: #ced4da;
|
||||||
|
--bs-gray-500: #adb5bd;
|
||||||
|
--bs-gray-600: #6c757d;
|
||||||
|
--bs-gray-700: #495057;
|
||||||
|
--bs-gray-800: #343a40;
|
||||||
|
--bs-gray-900: #212529;
|
||||||
|
--bs-primary: #0d6efd;
|
||||||
|
--bs-secondary: #6c757d;
|
||||||
|
--bs-success: #198754;
|
||||||
|
--bs-info: #0dcaf0;
|
||||||
|
--bs-warning: #ffc107;
|
||||||
|
--bs-danger: #dc3545;
|
||||||
|
--bs-light: #f8f9fa;
|
||||||
|
--bs-dark: #212529;
|
||||||
|
--bs-primary-rgb: 13, 110, 253;
|
||||||
|
--bs-secondary-rgb: 108, 117, 125;
|
||||||
|
--bs-success-rgb: 25, 135, 84;
|
||||||
|
--bs-info-rgb: 13, 202, 240;
|
||||||
|
--bs-warning-rgb: 255, 193, 7;
|
||||||
|
--bs-danger-rgb: 220, 53, 69;
|
||||||
|
--bs-light-rgb: 248, 249, 250;
|
||||||
|
--bs-dark-rgb: 33, 37, 41;
|
||||||
|
--bs-primary-text-emphasis: #052c65;
|
||||||
|
--bs-secondary-text-emphasis: #2b2f32;
|
||||||
|
--bs-success-text-emphasis: #0a3622;
|
||||||
|
--bs-info-text-emphasis: #055160;
|
||||||
|
--bs-warning-text-emphasis: #664d03;
|
||||||
|
--bs-danger-text-emphasis: #58151c;
|
||||||
|
--bs-light-text-emphasis: #495057;
|
||||||
|
--bs-dark-text-emphasis: #495057;
|
||||||
|
--bs-primary-bg-subtle: #cfe2ff;
|
||||||
|
--bs-secondary-bg-subtle: #e2e3e5;
|
||||||
|
--bs-success-bg-subtle: #d1e7dd;
|
||||||
|
--bs-info-bg-subtle: #cff4fc;
|
||||||
|
--bs-warning-bg-subtle: #fff3cd;
|
||||||
|
--bs-danger-bg-subtle: #f8d7da;
|
||||||
|
--bs-light-bg-subtle: #fcfcfd;
|
||||||
|
--bs-dark-bg-subtle: #ced4da;
|
||||||
|
--bs-primary-border-subtle: #9ec5fe;
|
||||||
|
--bs-secondary-border-subtle: #c4c8cb;
|
||||||
|
--bs-success-border-subtle: #a3cfbb;
|
||||||
|
--bs-info-border-subtle: #9eeaf9;
|
||||||
|
--bs-warning-border-subtle: #ffe69c;
|
||||||
|
--bs-danger-border-subtle: #f1aeb5;
|
||||||
|
--bs-light-border-subtle: #e9ecef;
|
||||||
|
--bs-dark-border-subtle: #adb5bd;
|
||||||
|
--bs-white-rgb: 255, 255, 255;
|
||||||
|
--bs-black-rgb: 0, 0, 0;
|
||||||
|
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||||
|
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||||
|
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
|
||||||
|
--bs-body-font-family: var(--bs-font-sans-serif);
|
||||||
|
--bs-body-font-size: 1rem;
|
||||||
|
--bs-body-font-weight: 400;
|
||||||
|
--bs-body-line-height: 1.5;
|
||||||
|
--bs-body-color: #212529;
|
||||||
|
--bs-body-color-rgb: 33, 37, 41;
|
||||||
|
--bs-body-bg: #fff;
|
||||||
|
--bs-body-bg-rgb: 255, 255, 255;
|
||||||
|
--bs-emphasis-color: #000;
|
||||||
|
--bs-emphasis-color-rgb: 0, 0, 0;
|
||||||
|
--bs-secondary-color: rgba(33, 37, 41, 0.75);
|
||||||
|
--bs-secondary-color-rgb: 33, 37, 41;
|
||||||
|
--bs-secondary-bg: #e9ecef;
|
||||||
|
--bs-secondary-bg-rgb: 233, 236, 239;
|
||||||
|
--bs-tertiary-color: rgba(33, 37, 41, 0.5);
|
||||||
|
--bs-tertiary-color-rgb: 33, 37, 41;
|
||||||
|
--bs-tertiary-bg: #f8f9fa;
|
||||||
|
--bs-tertiary-bg-rgb: 248, 249, 250;
|
||||||
|
--bs-link-color: #0d6efd;
|
||||||
|
--bs-link-color-rgb: 13, 110, 253;
|
||||||
|
--bs-link-decoration: underline;
|
||||||
|
--bs-link-hover-color: #0a58ca;
|
||||||
|
--bs-link-hover-color-rgb: 10, 88, 202;
|
||||||
|
--bs-code-color: #d63384;
|
||||||
|
--bs-highlight-bg: #fff3cd;
|
||||||
|
--bs-border-width: 1px;
|
||||||
|
--bs-border-style: solid;
|
||||||
|
--bs-border-color: #dee2e6;
|
||||||
|
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
|
||||||
|
--bs-border-radius: 0.375rem;
|
||||||
|
--bs-border-radius-sm: 0.25rem;
|
||||||
|
--bs-border-radius-lg: 0.5rem;
|
||||||
|
--bs-border-radius-xl: 1rem;
|
||||||
|
--bs-border-radius-xxl: 2rem;
|
||||||
|
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
|
||||||
|
--bs-border-radius-pill: 50rem;
|
||||||
|
--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
|
||||||
|
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
||||||
|
--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
|
||||||
|
--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||||
|
--bs-focus-ring-width: 0.25rem;
|
||||||
|
--bs-focus-ring-opacity: 0.25;
|
||||||
|
--bs-focus-ring-color: rgba(13, 110, 253, 0.25);
|
||||||
|
--bs-form-valid-color: #198754;
|
||||||
|
--bs-form-valid-border-color: #198754;
|
||||||
|
--bs-form-invalid-color: #dc3545;
|
||||||
|
--bs-form-invalid-border-color: #dc3545;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-bs-theme=dark] {
|
||||||
|
color-scheme: dark;
|
||||||
|
--bs-body-color: #adb5bd;
|
||||||
|
--bs-body-color-rgb: 173, 181, 189;
|
||||||
|
--bs-body-bg: #212529;
|
||||||
|
--bs-body-bg-rgb: 33, 37, 41;
|
||||||
|
--bs-emphasis-color: #fff;
|
||||||
|
--bs-emphasis-color-rgb: 255, 255, 255;
|
||||||
|
--bs-secondary-color: rgba(173, 181, 189, 0.75);
|
||||||
|
--bs-secondary-color-rgb: 173, 181, 189;
|
||||||
|
--bs-secondary-bg: #343a40;
|
||||||
|
--bs-secondary-bg-rgb: 52, 58, 64;
|
||||||
|
--bs-tertiary-color: rgba(173, 181, 189, 0.5);
|
||||||
|
--bs-tertiary-color-rgb: 173, 181, 189;
|
||||||
|
--bs-tertiary-bg: #2b3035;
|
||||||
|
--bs-tertiary-bg-rgb: 43, 48, 53;
|
||||||
|
--bs-primary-text-emphasis: #6ea8fe;
|
||||||
|
--bs-secondary-text-emphasis: #a7acb1;
|
||||||
|
--bs-success-text-emphasis: #75b798;
|
||||||
|
--bs-info-text-emphasis: #6edff6;
|
||||||
|
--bs-warning-text-emphasis: #ffda6a;
|
||||||
|
--bs-danger-text-emphasis: #ea868f;
|
||||||
|
--bs-light-text-emphasis: #f8f9fa;
|
||||||
|
--bs-dark-text-emphasis: #dee2e6;
|
||||||
|
--bs-primary-bg-subtle: #031633;
|
||||||
|
--bs-secondary-bg-subtle: #161719;
|
||||||
|
--bs-success-bg-subtle: #051b11;
|
||||||
|
--bs-info-bg-subtle: #032830;
|
||||||
|
--bs-warning-bg-subtle: #332701;
|
||||||
|
--bs-danger-bg-subtle: #2c0b0e;
|
||||||
|
--bs-light-bg-subtle: #343a40;
|
||||||
|
--bs-dark-bg-subtle: #1a1d20;
|
||||||
|
--bs-primary-border-subtle: #084298;
|
||||||
|
--bs-secondary-border-subtle: #41464b;
|
||||||
|
--bs-success-border-subtle: #0f5132;
|
||||||
|
--bs-info-border-subtle: #087990;
|
||||||
|
--bs-warning-border-subtle: #997404;
|
||||||
|
--bs-danger-border-subtle: #842029;
|
||||||
|
--bs-light-border-subtle: #495057;
|
||||||
|
--bs-dark-border-subtle: #343a40;
|
||||||
|
--bs-link-color: #6ea8fe;
|
||||||
|
--bs-link-hover-color: #8bb9fe;
|
||||||
|
--bs-link-color-rgb: 110, 168, 254;
|
||||||
|
--bs-link-hover-color-rgb: 139, 185, 254;
|
||||||
|
--bs-code-color: #e685b5;
|
||||||
|
--bs-border-color: #495057;
|
||||||
|
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
|
||||||
|
--bs-form-valid-color: #75b798;
|
||||||
|
--bs-form-valid-border-color: #75b798;
|
||||||
|
--bs-form-invalid-color: #ea868f;
|
||||||
|
--bs-form-invalid-border-color: #ea868f;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
:root {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: var(--bs-body-font-family);
|
||||||
|
font-size: var(--bs-body-font-size);
|
||||||
|
font-weight: var(--bs-body-font-weight);
|
||||||
|
line-height: var(--bs-body-line-height);
|
||||||
|
color: var(--bs-body-color);
|
||||||
|
text-align: var(--bs-body-text-align);
|
||||||
|
background-color: var(--bs-body-bg);
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin: 1rem 0;
|
||||||
|
color: inherit;
|
||||||
|
border: 0;
|
||||||
|
border-top: var(--bs-border-width) solid;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6, h5, h4, h3, h2, h1 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.2;
|
||||||
|
color: var(--bs-heading-color, inherit);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: calc(1.375rem + 1.5vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: calc(1.325rem + 0.9vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h2 {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: calc(1.3rem + 0.6vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h3 {
|
||||||
|
font-size: 1.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: calc(1.275rem + 0.3vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h4 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
abbr[title] {
|
||||||
|
-webkit-text-decoration: underline dotted;
|
||||||
|
text-decoration: underline dotted;
|
||||||
|
cursor: help;
|
||||||
|
-webkit-text-decoration-skip-ink: none;
|
||||||
|
text-decoration-skip-ink: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
address {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-style: normal;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol,
|
||||||
|
ul {
|
||||||
|
padding-left: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol,
|
||||||
|
ul,
|
||||||
|
dl {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol ol,
|
||||||
|
ul ul,
|
||||||
|
ol ul,
|
||||||
|
ul ol {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dt {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 0.875em;
|
||||||
|
}
|
||||||
|
|
||||||
|
mark {
|
||||||
|
padding: 0.1875em;
|
||||||
|
background-color: var(--bs-highlight-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
position: relative;
|
||||||
|
font-size: 0.75em;
|
||||||
|
line-height: 0;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:not([href]):not([class]), a:not([href]):not([class]):hover {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre,
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: var(--bs-font-monospace);
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
display: block;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
overflow: auto;
|
||||||
|
font-size: 0.875em;
|
||||||
|
}
|
||||||
|
pre code {
|
||||||
|
font-size: inherit;
|
||||||
|
color: inherit;
|
||||||
|
word-break: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-size: 0.875em;
|
||||||
|
color: var(--bs-code-color);
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
a > code {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
kbd {
|
||||||
|
padding: 0.1875rem 0.375rem;
|
||||||
|
font-size: 0.875em;
|
||||||
|
color: var(--bs-body-bg);
|
||||||
|
background-color: var(--bs-body-color);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
kbd kbd {
|
||||||
|
padding: 0;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure {
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
img,
|
||||||
|
svg {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
caption-side: bottom;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
caption {
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
color: var(--bs-secondary-color);
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
text-align: inherit;
|
||||||
|
text-align: -webkit-match-parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead,
|
||||||
|
tbody,
|
||||||
|
tfoot,
|
||||||
|
tr,
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
border-color: inherit;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:focus:not(:focus-visible) {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
button,
|
||||||
|
select,
|
||||||
|
optgroup,
|
||||||
|
textarea {
|
||||||
|
margin: 0;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
select {
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[role=button] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
word-wrap: normal;
|
||||||
|
}
|
||||||
|
select:disabled {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
[type=button],
|
||||||
|
[type=reset],
|
||||||
|
[type=submit] {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
button:not(:disabled),
|
||||||
|
[type=button]:not(:disabled),
|
||||||
|
[type=reset]:not(:disabled),
|
||||||
|
[type=submit]:not(:disabled) {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-moz-focus-inner {
|
||||||
|
padding: 0;
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
min-width: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
legend {
|
||||||
|
float: left;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-size: calc(1.275rem + 0.3vw);
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
legend {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
legend + * {
|
||||||
|
clear: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-datetime-edit-fields-wrapper,
|
||||||
|
::-webkit-datetime-edit-text,
|
||||||
|
::-webkit-datetime-edit-minute,
|
||||||
|
::-webkit-datetime-edit-hour-field,
|
||||||
|
::-webkit-datetime-edit-day-field,
|
||||||
|
::-webkit-datetime-edit-month-field,
|
||||||
|
::-webkit-datetime-edit-year-field {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-inner-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type=search] {
|
||||||
|
outline-offset: -2px;
|
||||||
|
-webkit-appearance: textfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* rtl:raw:
|
||||||
|
[type="tel"],
|
||||||
|
[type="url"],
|
||||||
|
[type="email"],
|
||||||
|
[type="number"] {
|
||||||
|
direction: ltr;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-color-swatch-wrapper {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
font: inherit;
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
::file-selector-button {
|
||||||
|
font: inherit;
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
output {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*# sourceMappingURL=bootstrap-reboot.css.map */
|
1
assets/bootstrap/css/bootstrap-reboot.css.map
Normal file
6
assets/bootstrap/css/bootstrap-reboot.min.css
vendored
Normal file
1
assets/bootstrap/css/bootstrap-reboot.min.css.map
Normal file
588
assets/bootstrap/css/bootstrap-reboot.rtl.css
vendored
Normal file
|
@ -0,0 +1,588 @@
|
||||||
|
/*!
|
||||||
|
* Bootstrap Reboot v5.3.0-alpha3 (https://getbootstrap.com/)
|
||||||
|
* Copyright 2011-2023 The Bootstrap Authors
|
||||||
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||||
|
*/
|
||||||
|
:root,
|
||||||
|
[data-bs-theme=light] {
|
||||||
|
--bs-blue: #0d6efd;
|
||||||
|
--bs-indigo: #6610f2;
|
||||||
|
--bs-purple: #6f42c1;
|
||||||
|
--bs-pink: #d63384;
|
||||||
|
--bs-red: #dc3545;
|
||||||
|
--bs-orange: #fd7e14;
|
||||||
|
--bs-yellow: #ffc107;
|
||||||
|
--bs-green: #198754;
|
||||||
|
--bs-teal: #20c997;
|
||||||
|
--bs-cyan: #0dcaf0;
|
||||||
|
--bs-black: #000;
|
||||||
|
--bs-white: #fff;
|
||||||
|
--bs-gray: #6c757d;
|
||||||
|
--bs-gray-dark: #343a40;
|
||||||
|
--bs-gray-100: #f8f9fa;
|
||||||
|
--bs-gray-200: #e9ecef;
|
||||||
|
--bs-gray-300: #dee2e6;
|
||||||
|
--bs-gray-400: #ced4da;
|
||||||
|
--bs-gray-500: #adb5bd;
|
||||||
|
--bs-gray-600: #6c757d;
|
||||||
|
--bs-gray-700: #495057;
|
||||||
|
--bs-gray-800: #343a40;
|
||||||
|
--bs-gray-900: #212529;
|
||||||
|
--bs-primary: #0d6efd;
|
||||||
|
--bs-secondary: #6c757d;
|
||||||
|
--bs-success: #198754;
|
||||||
|
--bs-info: #0dcaf0;
|
||||||
|
--bs-warning: #ffc107;
|
||||||
|
--bs-danger: #dc3545;
|
||||||
|
--bs-light: #f8f9fa;
|
||||||
|
--bs-dark: #212529;
|
||||||
|
--bs-primary-rgb: 13, 110, 253;
|
||||||
|
--bs-secondary-rgb: 108, 117, 125;
|
||||||
|
--bs-success-rgb: 25, 135, 84;
|
||||||
|
--bs-info-rgb: 13, 202, 240;
|
||||||
|
--bs-warning-rgb: 255, 193, 7;
|
||||||
|
--bs-danger-rgb: 220, 53, 69;
|
||||||
|
--bs-light-rgb: 248, 249, 250;
|
||||||
|
--bs-dark-rgb: 33, 37, 41;
|
||||||
|
--bs-primary-text-emphasis: #052c65;
|
||||||
|
--bs-secondary-text-emphasis: #2b2f32;
|
||||||
|
--bs-success-text-emphasis: #0a3622;
|
||||||
|
--bs-info-text-emphasis: #055160;
|
||||||
|
--bs-warning-text-emphasis: #664d03;
|
||||||
|
--bs-danger-text-emphasis: #58151c;
|
||||||
|
--bs-light-text-emphasis: #495057;
|
||||||
|
--bs-dark-text-emphasis: #495057;
|
||||||
|
--bs-primary-bg-subtle: #cfe2ff;
|
||||||
|
--bs-secondary-bg-subtle: #e2e3e5;
|
||||||
|
--bs-success-bg-subtle: #d1e7dd;
|
||||||
|
--bs-info-bg-subtle: #cff4fc;
|
||||||
|
--bs-warning-bg-subtle: #fff3cd;
|
||||||
|
--bs-danger-bg-subtle: #f8d7da;
|
||||||
|
--bs-light-bg-subtle: #fcfcfd;
|
||||||
|
--bs-dark-bg-subtle: #ced4da;
|
||||||
|
--bs-primary-border-subtle: #9ec5fe;
|
||||||
|
--bs-secondary-border-subtle: #c4c8cb;
|
||||||
|
--bs-success-border-subtle: #a3cfbb;
|
||||||
|
--bs-info-border-subtle: #9eeaf9;
|
||||||
|
--bs-warning-border-subtle: #ffe69c;
|
||||||
|
--bs-danger-border-subtle: #f1aeb5;
|
||||||
|
--bs-light-border-subtle: #e9ecef;
|
||||||
|
--bs-dark-border-subtle: #adb5bd;
|
||||||
|
--bs-white-rgb: 255, 255, 255;
|
||||||
|
--bs-black-rgb: 0, 0, 0;
|
||||||
|
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||||
|
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||||
|
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
|
||||||
|
--bs-body-font-family: var(--bs-font-sans-serif);
|
||||||
|
--bs-body-font-size: 1rem;
|
||||||
|
--bs-body-font-weight: 400;
|
||||||
|
--bs-body-line-height: 1.5;
|
||||||
|
--bs-body-color: #212529;
|
||||||
|
--bs-body-color-rgb: 33, 37, 41;
|
||||||
|
--bs-body-bg: #fff;
|
||||||
|
--bs-body-bg-rgb: 255, 255, 255;
|
||||||
|
--bs-emphasis-color: #000;
|
||||||
|
--bs-emphasis-color-rgb: 0, 0, 0;
|
||||||
|
--bs-secondary-color: rgba(33, 37, 41, 0.75);
|
||||||
|
--bs-secondary-color-rgb: 33, 37, 41;
|
||||||
|
--bs-secondary-bg: #e9ecef;
|
||||||
|
--bs-secondary-bg-rgb: 233, 236, 239;
|
||||||
|
--bs-tertiary-color: rgba(33, 37, 41, 0.5);
|
||||||
|
--bs-tertiary-color-rgb: 33, 37, 41;
|
||||||
|
--bs-tertiary-bg: #f8f9fa;
|
||||||
|
--bs-tertiary-bg-rgb: 248, 249, 250;
|
||||||
|
--bs-link-color: #0d6efd;
|
||||||
|
--bs-link-color-rgb: 13, 110, 253;
|
||||||
|
--bs-link-decoration: underline;
|
||||||
|
--bs-link-hover-color: #0a58ca;
|
||||||
|
--bs-link-hover-color-rgb: 10, 88, 202;
|
||||||
|
--bs-code-color: #d63384;
|
||||||
|
--bs-highlight-bg: #fff3cd;
|
||||||
|
--bs-border-width: 1px;
|
||||||
|
--bs-border-style: solid;
|
||||||
|
--bs-border-color: #dee2e6;
|
||||||
|
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
|
||||||
|
--bs-border-radius: 0.375rem;
|
||||||
|
--bs-border-radius-sm: 0.25rem;
|
||||||
|
--bs-border-radius-lg: 0.5rem;
|
||||||
|
--bs-border-radius-xl: 1rem;
|
||||||
|
--bs-border-radius-xxl: 2rem;
|
||||||
|
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
|
||||||
|
--bs-border-radius-pill: 50rem;
|
||||||
|
--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
|
||||||
|
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
||||||
|
--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
|
||||||
|
--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||||
|
--bs-focus-ring-width: 0.25rem;
|
||||||
|
--bs-focus-ring-opacity: 0.25;
|
||||||
|
--bs-focus-ring-color: rgba(13, 110, 253, 0.25);
|
||||||
|
--bs-form-valid-color: #198754;
|
||||||
|
--bs-form-valid-border-color: #198754;
|
||||||
|
--bs-form-invalid-color: #dc3545;
|
||||||
|
--bs-form-invalid-border-color: #dc3545;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-bs-theme=dark] {
|
||||||
|
color-scheme: dark;
|
||||||
|
--bs-body-color: #adb5bd;
|
||||||
|
--bs-body-color-rgb: 173, 181, 189;
|
||||||
|
--bs-body-bg: #212529;
|
||||||
|
--bs-body-bg-rgb: 33, 37, 41;
|
||||||
|
--bs-emphasis-color: #fff;
|
||||||
|
--bs-emphasis-color-rgb: 255, 255, 255;
|
||||||
|
--bs-secondary-color: rgba(173, 181, 189, 0.75);
|
||||||
|
--bs-secondary-color-rgb: 173, 181, 189;
|
||||||
|
--bs-secondary-bg: #343a40;
|
||||||
|
--bs-secondary-bg-rgb: 52, 58, 64;
|
||||||
|
--bs-tertiary-color: rgba(173, 181, 189, 0.5);
|
||||||
|
--bs-tertiary-color-rgb: 173, 181, 189;
|
||||||
|
--bs-tertiary-bg: #2b3035;
|
||||||
|
--bs-tertiary-bg-rgb: 43, 48, 53;
|
||||||
|
--bs-primary-text-emphasis: #6ea8fe;
|
||||||
|
--bs-secondary-text-emphasis: #a7acb1;
|
||||||
|
--bs-success-text-emphasis: #75b798;
|
||||||
|
--bs-info-text-emphasis: #6edff6;
|
||||||
|
--bs-warning-text-emphasis: #ffda6a;
|
||||||
|
--bs-danger-text-emphasis: #ea868f;
|
||||||
|
--bs-light-text-emphasis: #f8f9fa;
|
||||||
|
--bs-dark-text-emphasis: #dee2e6;
|
||||||
|
--bs-primary-bg-subtle: #031633;
|
||||||
|
--bs-secondary-bg-subtle: #161719;
|
||||||
|
--bs-success-bg-subtle: #051b11;
|
||||||
|
--bs-info-bg-subtle: #032830;
|
||||||
|
--bs-warning-bg-subtle: #332701;
|
||||||
|
--bs-danger-bg-subtle: #2c0b0e;
|
||||||
|
--bs-light-bg-subtle: #343a40;
|
||||||
|
--bs-dark-bg-subtle: #1a1d20;
|
||||||
|
--bs-primary-border-subtle: #084298;
|
||||||
|
--bs-secondary-border-subtle: #41464b;
|
||||||
|
--bs-success-border-subtle: #0f5132;
|
||||||
|
--bs-info-border-subtle: #087990;
|
||||||
|
--bs-warning-border-subtle: #997404;
|
||||||
|
--bs-danger-border-subtle: #842029;
|
||||||
|
--bs-light-border-subtle: #495057;
|
||||||
|
--bs-dark-border-subtle: #343a40;
|
||||||
|
--bs-link-color: #6ea8fe;
|
||||||
|
--bs-link-hover-color: #8bb9fe;
|
||||||
|
--bs-link-color-rgb: 110, 168, 254;
|
||||||
|
--bs-link-hover-color-rgb: 139, 185, 254;
|
||||||
|
--bs-code-color: #e685b5;
|
||||||
|
--bs-border-color: #495057;
|
||||||
|
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
|
||||||
|
--bs-form-valid-color: #75b798;
|
||||||
|
--bs-form-valid-border-color: #75b798;
|
||||||
|
--bs-form-invalid-color: #ea868f;
|
||||||
|
--bs-form-invalid-border-color: #ea868f;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
:root {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: var(--bs-body-font-family);
|
||||||
|
font-size: var(--bs-body-font-size);
|
||||||
|
font-weight: var(--bs-body-font-weight);
|
||||||
|
line-height: var(--bs-body-line-height);
|
||||||
|
color: var(--bs-body-color);
|
||||||
|
text-align: var(--bs-body-text-align);
|
||||||
|
background-color: var(--bs-body-bg);
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin: 1rem 0;
|
||||||
|
color: inherit;
|
||||||
|
border: 0;
|
||||||
|
border-top: var(--bs-border-width) solid;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6, h5, h4, h3, h2, h1 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.2;
|
||||||
|
color: var(--bs-heading-color, inherit);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: calc(1.375rem + 1.5vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: calc(1.325rem + 0.9vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h2 {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: calc(1.3rem + 0.6vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h3 {
|
||||||
|
font-size: 1.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: calc(1.275rem + 0.3vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h4 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
abbr[title] {
|
||||||
|
-webkit-text-decoration: underline dotted;
|
||||||
|
text-decoration: underline dotted;
|
||||||
|
cursor: help;
|
||||||
|
-webkit-text-decoration-skip-ink: none;
|
||||||
|
text-decoration-skip-ink: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
address {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-style: normal;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol,
|
||||||
|
ul {
|
||||||
|
padding-right: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol,
|
||||||
|
ul,
|
||||||
|
dl {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol ol,
|
||||||
|
ul ul,
|
||||||
|
ol ul,
|
||||||
|
ul ol {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dt {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 0.875em;
|
||||||
|
}
|
||||||
|
|
||||||
|
mark {
|
||||||
|
padding: 0.1875em;
|
||||||
|
background-color: var(--bs-highlight-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
position: relative;
|
||||||
|
font-size: 0.75em;
|
||||||
|
line-height: 0;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:not([href]):not([class]), a:not([href]):not([class]):hover {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre,
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: var(--bs-font-monospace);
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
display: block;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
overflow: auto;
|
||||||
|
font-size: 0.875em;
|
||||||
|
}
|
||||||
|
pre code {
|
||||||
|
font-size: inherit;
|
||||||
|
color: inherit;
|
||||||
|
word-break: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-size: 0.875em;
|
||||||
|
color: var(--bs-code-color);
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
a > code {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
kbd {
|
||||||
|
padding: 0.1875rem 0.375rem;
|
||||||
|
font-size: 0.875em;
|
||||||
|
color: var(--bs-body-bg);
|
||||||
|
background-color: var(--bs-body-color);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
kbd kbd {
|
||||||
|
padding: 0;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure {
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
img,
|
||||||
|
svg {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
caption-side: bottom;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
caption {
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
color: var(--bs-secondary-color);
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
text-align: inherit;
|
||||||
|
text-align: -webkit-match-parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead,
|
||||||
|
tbody,
|
||||||
|
tfoot,
|
||||||
|
tr,
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
border-color: inherit;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:focus:not(:focus-visible) {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
button,
|
||||||
|
select,
|
||||||
|
optgroup,
|
||||||
|
textarea {
|
||||||
|
margin: 0;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
select {
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[role=button] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
word-wrap: normal;
|
||||||
|
}
|
||||||
|
select:disabled {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
[type=button],
|
||||||
|
[type=reset],
|
||||||
|
[type=submit] {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
button:not(:disabled),
|
||||||
|
[type=button]:not(:disabled),
|
||||||
|
[type=reset]:not(:disabled),
|
||||||
|
[type=submit]:not(:disabled) {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-moz-focus-inner {
|
||||||
|
padding: 0;
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
min-width: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
legend {
|
||||||
|
float: right;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-size: calc(1.275rem + 0.3vw);
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
legend {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
legend + * {
|
||||||
|
clear: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-datetime-edit-fields-wrapper,
|
||||||
|
::-webkit-datetime-edit-text,
|
||||||
|
::-webkit-datetime-edit-minute,
|
||||||
|
::-webkit-datetime-edit-hour-field,
|
||||||
|
::-webkit-datetime-edit-day-field,
|
||||||
|
::-webkit-datetime-edit-month-field,
|
||||||
|
::-webkit-datetime-edit-year-field {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-inner-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type=search] {
|
||||||
|
outline-offset: -2px;
|
||||||
|
-webkit-appearance: textfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type="tel"],
|
||||||
|
[type="url"],
|
||||||
|
[type="email"],
|
||||||
|
[type="number"] {
|
||||||
|
direction: ltr;
|
||||||
|
}
|
||||||
|
::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-color-swatch-wrapper {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
font: inherit;
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
::file-selector-button {
|
||||||
|
font: inherit;
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
output {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
/*# sourceMappingURL=bootstrap-reboot.rtl.css.map */
|
1
assets/bootstrap/css/bootstrap-reboot.rtl.css.map
Normal file
6
assets/bootstrap/css/bootstrap-reboot.rtl.min.css
vendored
Normal file
1
assets/bootstrap/css/bootstrap-reboot.rtl.min.css.map
Normal file
5397
assets/bootstrap/css/bootstrap-utilities.css
vendored
Normal file
1
assets/bootstrap/css/bootstrap-utilities.css.map
Normal file
6
assets/bootstrap/css/bootstrap-utilities.min.css
vendored
Normal file
1
assets/bootstrap/css/bootstrap-utilities.min.css.map
Normal file
5388
assets/bootstrap/css/bootstrap-utilities.rtl.css
vendored
Normal file
1
assets/bootstrap/css/bootstrap-utilities.rtl.css.map
Normal file
6
assets/bootstrap/css/bootstrap-utilities.rtl.min.css
vendored
Normal file
1
assets/bootstrap/css/bootstrap-utilities.rtl.min.css.map
Normal file
12113
assets/bootstrap/css/bootstrap.css
vendored
Normal file
1
assets/bootstrap/css/bootstrap.css.map
Normal file
6
assets/bootstrap/css/bootstrap.min.css
vendored
Normal file
1
assets/bootstrap/css/bootstrap.min.css.map
Normal file
12077
assets/bootstrap/css/bootstrap.rtl.css
vendored
Normal file
1
assets/bootstrap/css/bootstrap.rtl.css.map
Normal file
6
assets/bootstrap/css/bootstrap.rtl.min.css
vendored
Normal file
1
assets/bootstrap/css/bootstrap.rtl.min.css.map
Normal file
6295
assets/bootstrap/js/bootstrap.bundle.js
vendored
Normal file
1
assets/bootstrap/js/bootstrap.bundle.js.map
Normal file
7
assets/bootstrap/js/bootstrap.bundle.min.js
vendored
Normal file
1
assets/bootstrap/js/bootstrap.bundle.min.js.map
Normal file
4423
assets/bootstrap/js/bootstrap.esm.js
vendored
Normal file
1
assets/bootstrap/js/bootstrap.esm.js.map
Normal file
7
assets/bootstrap/js/bootstrap.esm.min.js
vendored
Normal file
1
assets/bootstrap/js/bootstrap.esm.min.js.map
Normal file
4469
assets/bootstrap/js/bootstrap.js
vendored
Normal file
1
assets/bootstrap/js/bootstrap.js.map
Normal file
7
assets/bootstrap/js/bootstrap.min.js
vendored
Normal file
1
assets/bootstrap/js/bootstrap.min.js.map
Normal file
15
assets/email.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// This code de-obfuscates my email and displays it on screen when the button is clicked
|
||||||
|
// It's not a perfect solution, but it should stop be enough to stop email scraping
|
||||||
|
// Email encoded in base64. This might be changed in the future to prevent scraping targeting base64 strings
|
||||||
|
const email = "Y29udGFjdEBvd2Vucnlhbi51cwo=";
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
// Get document elements
|
||||||
|
const button = document.querySelector("#emailButton");
|
||||||
|
const emailDiv = document.querySelector("#email");
|
||||||
|
// Decode the email string and insert a mailto link into the DOM, then disable the button
|
||||||
|
button.addEventListener("click", function () {
|
||||||
|
const decodedEmail = atob(email);
|
||||||
|
emailDiv.insertAdjacentHTML("beforeend", "<div class=\"col\"><a href=\"mailto:".concat(decodedEmail, "\"><strong>").concat(decodedEmail, "</strong></a></div>"));
|
||||||
|
button.disabled = true;
|
||||||
|
});
|
||||||
|
});
|
17
assets/email.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// This code de-obfuscates my email and displays it on screen when the button is clicked
|
||||||
|
// It's not a perfect solution, but it should stop be enough to stop email scraping
|
||||||
|
|
||||||
|
// Email encoded in base64. This might be changed in the future to prevent scraping targeting base64 strings
|
||||||
|
const email: string = "Y29udGFjdEBvd2Vucnlhbi51cwo=";
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
// Get document elements
|
||||||
|
const button: HTMLButtonElement = document.querySelector("#emailButton");
|
||||||
|
const emailDiv: HTMLDivElement = document.querySelector("#email");
|
||||||
|
|
||||||
|
// Decode the email string and insert a mailto link into the DOM, then disable the button
|
||||||
|
button.addEventListener("click", () => {
|
||||||
|
const decodedEmail: string = atob(email);
|
||||||
|
emailDiv.insertAdjacentHTML("beforeend", `<div class="col"><a href="mailto:${decodedEmail}"><strong>${decodedEmail}</strong></a></div>`);
|
||||||
|
button.disabled = true;
|
||||||
|
})
|
||||||
|
});
|
BIN
assets/images/calculators.webp
Normal file
After Width: | Height: | Size: 1 MiB |
BIN
assets/images/graphlinkadapter.webp
Normal file
After Width: | Height: | Size: 1,017 KiB |
BIN
assets/images/informinator.webp
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
assets/images/jambox.webp
Normal file
After Width: | Height: | Size: 169 KiB |
BIN
assets/images/resistorcomparison.webp
Normal file
After Width: | Height: | Size: 106 KiB |
BIN
assets/images/website.webp
Normal file
After Width: | Height: | Size: 70 KiB |
14
assets/landingpage.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// JS Code that rotates the gradient on the content box on the front page
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
const mainBox = document.querySelector("#rotating-gradient");
|
||||||
|
let angle = 0;
|
||||||
|
// Rotate the gradient 1 degree every 100ms
|
||||||
|
setInterval(function () {
|
||||||
|
const gradient = "linear-gradient(".concat(angle, "deg, #1E1F46, #404040)");
|
||||||
|
mainBox.style["background"] = gradient;
|
||||||
|
angle += 1;
|
||||||
|
if (angle >= 360) {
|
||||||
|
angle = 0;
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
});
|
16
assets/landingpage.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// JS Code that rotates the gradient on the content box on the front page
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", (): void => {
|
||||||
|
const mainBox: HTMLDivElement = document.querySelector("#rotating-gradient");
|
||||||
|
let angle: number = 0;
|
||||||
|
|
||||||
|
// Rotate the gradient 1 degree every 100ms
|
||||||
|
setInterval((): void => {
|
||||||
|
const gradient: string = `linear-gradient(${angle}deg, #1E1F46, #404040)`;
|
||||||
|
mainBox.style["background"] = gradient;
|
||||||
|
angle += 1;
|
||||||
|
if (angle >= 360) {
|
||||||
|
angle = 0
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
});
|
9
assets/utils.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
// Some useful functions used throughout my code
|
||||||
|
// Based on this SO answer https://stackoverflow.com/a/35880730/9523246
|
||||||
|
function lazyLoadCSS(url) {
|
||||||
|
const css = document.createElement('link');
|
||||||
|
css.href = url;
|
||||||
|
css.rel = 'stylesheet';
|
||||||
|
css.type = 'text/css';
|
||||||
|
document.getElementsByTagName('head')[0].appendChild(css);
|
||||||
|
}
|
10
assets/utils.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// Some useful functions used throughout my code
|
||||||
|
|
||||||
|
// Based on this SO answer https://stackoverflow.com/a/35880730/9523246
|
||||||
|
function lazyLoadCSS(url: string): void {
|
||||||
|
const css: HTMLLinkElement = document.createElement('link');
|
||||||
|
css.href = url;
|
||||||
|
css.rel = 'stylesheet';
|
||||||
|
css.type = 'text/css';
|
||||||
|
document.getElementsByTagName('head')[0].appendChild(css);
|
||||||
|
}
|
BIN
assets/videos/calculatorperf.webm
Normal file
18
contact.html
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
---
|
||||||
|
layout: main
|
||||||
|
permalink: /contact
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<p>Thank you for choosing to contact me!</p>
|
||||||
|
<p>To prevent email scraping, my contact email is hidden behind this button.</p>
|
||||||
|
</div>
|
||||||
|
<div id="email" class="row d-inline-flex">
|
||||||
|
<div class="col">
|
||||||
|
<button id="emailButton" type="button" class="btn btn-primary">Get contact email</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/assets/email.js"></script>
|
7
css/style.scss
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
# Convert SASS into CSS
|
||||||
|
# Source: https://avic.devpractical.com/jekyll-sass/
|
||||||
|
---
|
||||||
|
|
||||||
|
// Imports styles in the file located at _sass/style.sass
|
||||||
|
@import "style.sass";
|
BIN
favicon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
17
homepage.gemspec
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
Gem::Specification.new do |spec|
|
||||||
|
spec.name = "homepage"
|
||||||
|
spec.version = "0.1.0"
|
||||||
|
spec.authors = ["Owen Ryan"]
|
||||||
|
spec.email = ["owen@owenryan.us"]
|
||||||
|
|
||||||
|
spec.summary = "Jekyll theme used for owenryan.us"
|
||||||
|
spec.homepage = "https://code.owenryan.us/owenryan/owenryan.us"
|
||||||
|
spec.license = "MIT"
|
||||||
|
|
||||||
|
spec.files = `git ls-files -z`.split("\x0").select { |f| f.match(%r!^(assets|_data|_layouts|_includes|_sass|LICENSE|README|_config\.yml)!i) }
|
||||||
|
|
||||||
|
spec.add_runtime_dependency "jekyll", "~> 4.3"
|
||||||
|
spec.add_runtime_dependency "jekyll-sass-converter", "~> 3.0"
|
||||||
|
end
|
14
index.html
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
layout: main
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="container my-5">
|
||||||
|
<div id="rotating-gradient" class="col-7 p-5 text-center rounded-3 position-absolute top-50 start-50 translate-middle">
|
||||||
|
<h1 class="text-body-emphasis">Hello there!</h1>
|
||||||
|
<p class="col-lg-8 mx-auto fs-5 text-muted">Welcome to my website!</p>
|
||||||
|
|
||||||
|
{% include social-media-icons.html %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/assets/landingpage.js"></script>
|
22
projects.html
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
---
|
||||||
|
layout: main
|
||||||
|
permalink: /projects
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
{%- for post in site.posts -%}
|
||||||
|
<div class="col p-2">
|
||||||
|
<div class="card project-card">
|
||||||
|
{%- if post.thumbnail_url -%}
|
||||||
|
<img src="{{ post.thumbnail_url }}" class="card-img-top" alt="{{ post.title }} thumbnail">
|
||||||
|
{%- endif -%}
|
||||||
|
<div class="card-body">
|
||||||
|
<a class="card-title text-body-emphasis" href="{{ post.url }}">{{ post.title }}</a>
|
||||||
|
<p class="card-text">{{ post.description }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{%- endfor -%}
|
||||||
|
</div>
|
||||||
|
</div>
|