Compare commits

...

14 Commits

53 changed files with 1852 additions and 370 deletions

View File

@@ -0,0 +1,10 @@
{
"permissions": {
"allow": [
"Bash(grep:*)",
"Bash(cat:*)",
"Bash(find:*)"
]
},
"outputStyle": "default"
}

View File

@@ -1,5 +1,5 @@
# Sample workflow for building and deploying a Hugo site to GitHub Pages # Workflow for building and deploying a Quarto site to GitHub Pages
name: Deploy Hugo site to Pages name: Deploy Quarto site to Pages
on: on:
# Runs on pushes targeting the default branch # Runs on pushes targeting the default branch
@@ -30,36 +30,36 @@ jobs:
# Build job # Build job
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env:
HUGO_VERSION: 0.148.1
steps: steps:
- name: Install Hugo CLI
run: |
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
&& sudo dpkg -i ${{ runner.temp }}/hugo.deb
- name: Install Dart Sass
run: sudo snap install dart-sass
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Setup Quarto
uses: quarto-dev/quarto-actions/setup@v2
with: with:
submodules: recursive version: release
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- name: Install Python dependencies
run: |
pip install jupyter
- name: Setup Pages - name: Setup Pages
id: pages id: pages
uses: actions/configure-pages@v5 uses: actions/configure-pages@v5
- name: Install Node.js dependencies
run: "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true" - name: Render Quarto site
- name: Build with Hugo run: quarto render
env:
HUGO_CACHEDIR: ${{ runner.temp }}/hugo_cache
HUGO_ENVIRONMENT: production
run: |
hugo \
--minify \
--baseURL "${{ steps.pages.outputs.base_url }}/"
- name: Upload artifact - name: Upload artifact
uses: actions/upload-pages-artifact@v3 uses: actions/upload-pages-artifact@v3
with: with:
path: ./public path: ./_site
# Deployment job # Deployment job
deploy: deploy:

5
.gitignore vendored
View File

@@ -9,3 +9,8 @@ hugo.darwin
.DS_Store .DS_Store
.vscode/ .vscode/
# Quarto
/.quarto/
/_site/
.venv/

6
.gitmodules vendored
View File

@@ -1,6 +0,0 @@
[submodule "themes/ananke"]
path = themes/ananke
url = https://github.com/theNewDynamic/gohugo-theme-ananke.git
[submodule "themes/PaperMod"]
path = themes/PaperMod
url = https://github.com/adityatelange/hugo-PaperMod.git

10
404.qmd Normal file
View File

@@ -0,0 +1,10 @@
---
title: "Page Not Found"
page-layout: full
---
## Page Not Found
The page you're looking for doesn't exist.
[Return to homepage](/)

66
MIGRATION-CHECKLIST.md Normal file
View File

@@ -0,0 +1,66 @@
# Migration Checklist
## 📋 Content Migration
### Blog Posts (8 total)
- [ ] `dataviz-blogs.md``posts/dataviz-blogs/index.qmd` (lang: uk)
- [ ] `dataviz-books-ua.md``posts/dataviz-books-ua/index.qmd` (lang: uk)
- [ ] `entso-e-ukraine-energy-transfer-viz.md``posts/entso-e-ukraine-energy-transfer-viz/index.qmd` (lang: uk, **showcase**)
- [ ] `forbes-50-foundations.md``posts/forbes-50-foundations/index.qmd` (lang: uk, **showcase**)
- [ ] `my-first-post.md``posts/my-first-post/index.qmd` (lang: uk)
- [ ] `vega-altair-hugo-shortcodes.md``posts/vega-altair-hugo-shortcodes/index.qmd` (lang: uk, **showcase**)
- [ ] `bar-charts-makeover.md` (English) → `posts/bar-charts-makeover/index.qmd` (lang: en, **showcase**)
- [ ] Convert all Vega-Lite shortcodes to code fences
### TILs (2 total)
- [ ] `inspired-by-blogs.md``til/inspired-by-blogs.qmd` (lang: uk)
- [ ] `obsidian-paperless.md``til/obsidian-paperless.qmd` (lang: uk)
## 🎨 Customization
- [ ] Update `about/index.qmd` with your actual bio
- [ ] Add contact information to About page
- [ ] Add any real projects to `projects/` section
- [ ] Customize homepage (`index.qmd`) if desired
- [ ] Review and adjust theme colors in `styles.css` if needed
## 🧹 Cleanup
- [ ] Delete `posts/sample-ukrainian-post/`
- [ ] Delete `posts/sample-english-post/`
- [ ] Delete `posts/sample-visualization-post/`
- [ ] Delete `til/sample-til-ukrainian.qmd`
- [ ] Delete `til/sample-til-english.qmd`
- [ ] Delete `projects/sample-project-1/`
## ✅ Testing
- [ ] Run `quarto preview` and check all pages
- [ ] Verify all blog posts render correctly
- [ ] Verify all TILs render correctly
- [ ] Check all Vega-Lite visualizations display properly
- [ ] Test portfolio page shows showcase items
- [ ] Test navigation between all sections
- [ ] Check responsive design on mobile
- [ ] Verify analytics scripts load (check browser console)
## 🚀 Deployment
- [ ] Run `quarto render` successfully
- [ ] Check build output in `_site/`
- [ ] Test old `/en/*` URL redirects
- [ ] Deploy `_site/` to hosting
- [ ] Verify production site works correctly
- [ ] Update DNS if needed
## 🗑️ Final Cleanup (After Successful Deployment)
- [ ] Archive or delete Hugo `content/` directory
- [ ] Delete `archetypes/`, `layouts/`, `themes/`
- [ ] Delete `hugo.yaml`
- [ ] Delete `public/`, `resources/`
- [ ] Keep `.git/` and version control as-is
---
**Tip:** Test with one post first to validate the workflow before batch-migrating everything!

250
MIGRATION-README.md Normal file
View File

@@ -0,0 +1,250 @@
# Hugo to Quarto Migration Guide
## ✅ Migration Status: Infrastructure Complete
The Quarto site infrastructure is now fully set up! The site builds successfully and is ready for content migration.
## 🏗️ What's Been Created
### Configuration
- `_quarto.yml` - Main site configuration with navbar, footer, analytics, **Vega-Lite filter**
- `vega-lite.lua` - **Lua filter for Vega-Lite support** (Quarto doesn't have native support)
- `styles.css` - Minimal custom styling (Vega chart centering)
- `_includes/analytics.html` - Plausible + Matomo analytics
- `_redirects` - URL redirects for old `/en/*` paths
- `.gitignore` - Updated with Quarto-specific entries
### Directory Structure
```
├── posts/ # Blog posts (3 sample placeholders)
├── til/ # TILs (2 sample placeholders)
├── portfolio/ # Portfolio listing page
├── projects/ # Projects section (1 sample project)
├── about/ # About page template
└── index.qmd # Homepage
```
### Sample Content (Placeholders)
- `posts/sample-ukrainian-post/` - Example Ukrainian post structure
- `posts/sample-english-post/` - Example English post structure
- `posts/sample-visualization-post/` - Example with Vega-Lite chart
- `til/sample-til-ukrainian.qmd` - Example Ukrainian TIL
- `til/sample-til-english.qmd` - Example English TIL
- `projects/sample-project-1/` - Example full project page
## 📝 Manual Migration Steps
### 1. Migrate Blog Posts (8 posts)
For each Hugo post in `content/posts/` and `content/en/posts/`:
1. **Create directory:** `posts/{post-slug}/`
2. **Convert file:** Copy `.md``index.qmd` in the new directory
3. **Update frontmatter:**
```yaml
---
title: "Your Title"
date: "YYYY-MM-DD"
lang: uk # or 'en' for English posts
categories:
- dataviz
- showcase # Add this for portfolio items!
description: "Brief description"
---
```
4. **Convert Vega-Lite shortcodes:**
- Remove: `{{</* vega-lite id="..." */>}}...{{</* /vega-lite */>}}`
- Replace with:
````markdown
```{vega-lite}
{your vega spec here}
```
````
- See `VEGA-LITE-SETUP.md` for detailed instructions
**Posts to migrate:**
- `content/posts/dataviz-blogs.md` → `posts/dataviz-blogs/index.qmd`
- `content/posts/dataviz-books-ua.md` → `posts/dataviz-books-ua/index.qmd`
- `content/posts/entso-e-ukraine-energy-transfer-viz.md` → `posts/entso-e-ukraine-energy-transfer-viz/index.qmd`
- `content/posts/forbes-50-foundations.md` → `posts/forbes-50-foundations/index.qmd` **(add 'showcase' category)**
- `content/posts/my-first-post.md` → `posts/my-first-post/index.qmd`
- `content/posts/vega-altair-hugo-shortcodes.md` → `posts/vega-altair-hugo-shortcodes/index.qmd` **(add 'showcase' category)**
- `content/en/posts/bar-charts-makeover.md` → `posts/bar-charts-makeover/index.qmd` **(add 'showcase' category, lang: en)**
### 2. Migrate TILs (2 posts)
For each Hugo TIL in `content/til/`:
1. **Convert file:** Copy `.md` → `.qmd` directly in `til/` folder
2. **Update frontmatter:**
```yaml
---
title: "Your Title"
date: "YYYY-MM-DD"
lang: uk
categories:
- tools
---
```
**TILs to migrate:**
- `content/til/inspired-by-blogs.md` → `til/inspired-by-blogs.qmd`
- `content/til/obsidian-paperless.md` → `til/obsidian-paperless.qmd`
### 3. Posts/TILs Auto-Discovery
✅ **Good news!** The listing pages automatically discover new posts and TILs:
- **Posts:** Any directory with `index.qmd` in `posts/` appears automatically
- **TILs:** Any `.qmd` file in `til/` appears automatically
- **No manual updates needed!** Just add your files and they'll show up
### 4. Update Portfolio Page
The portfolio page needs manual updates. Edit `portfolio/index.qmd` and add your showcase posts:
```markdown
::: {.g-col-12 .g-col-md-6}
### [Forbes Top-50 Foundations](../posts/forbes-50-foundations/)
Interactive visualization of Ukraine's top charitable foundations
:::
::: {.g-col-12 .g-col-md-6}
### [Bar Charts Makeover](../posts/bar-charts-makeover/)
Seven ways to visualize year-over-year growth
:::
```
### 5. Delete Sample Placeholders
After migrating and updating listings, delete these placeholder files:
```bash
rm -rf posts/sample-*
rm til/sample-*
rm -rf projects/sample-project-1
```
### 6. Customize About Page
Edit `about/index.qmd` and replace template content with:
- Your bio
- Skills and expertise
- Contact information
- Links to social profiles
### 7. Add Real Projects
Option A: Full project page
```bash
mkdir projects/your-project-name
# Create projects/your-project-name/index.qmd
```
Option B: Just list in `projects/index.qmd`
## 🔧 Key Technical Details
### Language Tags
Use `lang: uk` or `lang: en` in frontmatter to tag content language. This allows filtering by language if needed.
### Portfolio Items
Add `showcase` to the categories list for any post you want in your portfolio:
```yaml
categories:
- dataviz
- showcase # This makes it appear in portfolio
```
### Vega-Lite Visualizations
Enabled via `vega-lite.lua` filter (already configured). Use:
````markdown
```{vega-lite}
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"mark": "bar",
...
}
```
````
Optional attributes:
- `#| echo: false` - Hide code, show only visualization
- `#| code-fold: true` - Make code collapsible
See `VEGA-LITE-SETUP.md` for complete documentation.
## 🚀 Building & Previewing
### Local Preview
```bash
quarto preview
```
Opens at `http://localhost:4848`
### Build Site
```bash
quarto render
```
Output goes to `_site/`
### Check for Errors
```bash
quarto check
```
## 📂 What to Keep vs Delete
### Keep (Quarto):
- All `*.qmd` files
- `_quarto.yml`
- `styles.css`
- `_includes/`
- `_redirects`
- `_site/` (build output)
### Can Eventually Delete (Hugo):
- `archetypes/`
- `content/` (after migrating to `posts/` and `til/`)
- `data/`
- `i18n/`
- `layouts/`
- `public/`
- `resources/`
- `static/`
- `templates/`
- `themes/`
- `hugo.yaml`
**Don't delete Hugo files yet!** Wait until you've confirmed the Quarto migration is complete and deployed.
## 🌐 Deployment
1. Build: `quarto render`
2. Deploy `_site/` directory to your hosting
3. Ensure `_redirects` file is included for old URL support
## 📌 Important Notes
- **URL Preservation:** Old `/en/posts/*` URLs will redirect to `/posts/*` via `_redirects`
- **Analytics:** Already integrated (Plausible + Matomo)
- **No Language Duplication:** All posts in one location, tagged with `lang` field
- **Sample Content:** Delete all `sample-*` files after migrating real content
## ✨ Benefits You'll Gain
1. ✅ Vega-Lite support via Lua filter (cleaner than Hugo shortcodes)
2. ✅ Can add Python/R code that generates visualizations
3. ✅ Observable JS for interactive dashboards
4. ✅ Better data analysis workflows
5. ✅ Single source of truth (no language directory duplication)
6. ✅ Cleaner, more maintainable structure
## 📚 Additional Documentation
- **`VEGA-LITE-SETUP.md`** - Complete guide to Vega-Lite filter and usage
- **`QUICK-START.md`** - How to use Quarto, add posts, common commands
- **`MIGRATION-CHECKLIST.md`** - Checkbox list to track migration progress
---
**Ready to migrate!** Start with one post to test the workflow, then batch-migrate the rest.

132
QUICK-START.md Normal file
View File

@@ -0,0 +1,132 @@
# Quick Start Guide
## ✅ Setup is Complete!
The Quarto site infrastructure is ready. Here's how to work with it:
## 🚀 Preview the Site
```bash
quarto preview
```
Opens at http://localhost:4848 with live reload.
## 🎨 Theme & Styling
- **Theme:** Cosmo (clean, minimal Bootstrap theme)
- **Custom CSS:** `styles.css` (minimal - just Vega chart centering)
- To change theme, edit `_quarto.yml` and try: cosmo, flatly, litera, minty, etc.
## 📝 Adding Your First Real Post
1. **Create post directory:**
```bash
mkdir posts/your-post-slug
```
2. **Create `posts/your-post-slug/index.qmd`:**
```yaml
---
title: "Your Post Title"
date: "2024-03-15"
lang: uk # or 'en'
categories:
- dataviz
- showcase # Include for portfolio items
description: "Brief description"
---
Your content here...
```
3. **Preview changes** (posts auto-discovered!):
```bash
quarto preview
```
✅ Your new post will automatically appear in the listing - no manual updates needed!
## 📊 Using Vega-Lite Visualizations
**Important:** This project uses `vega-lite.lua` filter for Vega-Lite support.
**Hugo shortcode (OLD):**
```markdown
{{</* vega-lite id="chart1" */>}}
{vega spec}
{{</* /vega-lite */>}}
```
**Quarto with Lua filter (NEW):**
````markdown
```{vega-lite}
{vega spec}
```
````
**With options:**
````markdown
```{vega-lite}
#| echo: false
#| code-fold: true
{vega spec}
```
````
See **`VEGA-LITE-SETUP.md`** for complete documentation and examples.
## 🔧 Common Commands
```bash
# Preview with live reload
quarto preview
# Build the site
quarto render
# Check for issues
quarto check
# Clean build cache
quarto clean
```
## 📂 Where Things Go
- **Blog posts:** `posts/post-slug/index.qmd`
- **TILs:** `til/post-name.qmd`
- **Projects:** `projects/project-name/index.qmd` (or just list in `projects/index.qmd`)
- **About page:** `about/index.qmd`
- **Homepage:** `index.qmd`
## ⚠️ Important Notes
1. **Use `lang: uk` or `lang: en`** (not `language:` - that's reserved by Quarto)
2. **Update listing pages** when you add real content (see MIGRATION-README.md)
3. **Posts with `showcase` category** automatically appear in portfolio
4. **Delete sample files** after migrating your real content
## 🆘 Troubleshooting
**Error: "list needs to have at least one item"**
- Update the listing `contents:` in the index page to reference actual files/directories
**Vega chart not showing:**
- Check the JSON is valid
- Ensure you're using ````{vega-lite}` code fence (not `{json}`)
- Check browser console for errors
**Styles look wrong:**
- Run `quarto clean && quarto render` to rebuild from scratch
- Check `styles.css` hasn't been corrupted
## 📚 Next Steps
1. Test by migrating one post from Hugo
2. Verify it displays correctly
3. Batch migrate the rest
4. Update all listing pages
5. Delete sample placeholders
6. Deploy!
See `MIGRATION-README.md` for detailed migration instructions.

182
VEGA-LITE-SETUP.md Normal file
View File

@@ -0,0 +1,182 @@
# Vega-Lite Setup with Quarto
## Important: Vega-Lite Lua Filter Required
**Quarto does NOT have native Vega-Lite support out of the box.** This project uses `vega-lite.lua` - a Quarto filter that enables Vega-Lite visualization rendering.
## ✅ What's Already Configured
1. **`vega-lite.lua`** - Custom Lua filter that processes Vega-Lite code blocks
2. **`_quarto.yml`** - Already configured to use the filter:
```yaml
filters:
- vega-lite.lua
```
## 🎨 How to Use Vega-Lite in Your Posts
### Basic Usage
````markdown
```{.vega-lite}
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": {
"values": [
{"category": "A", "value": 28},
{"category": "B", "value": 55}
]
},
"mark": "bar",
"encoding": {
"x": {"field": "category", "type": "nominal"},
"y": {"field": "value", "type": "quantitative"}
}
}
```
````
### With Options
````markdown
```{.vega-lite echo=false}
{your vega spec}
```
````
**Supported options:**
- `echo=false` - Hide the code, show only the visualization
- `echo=true` - Show code (default)
- `code-fold=true` - Make code collapsible and collapsed (default)
- `code-fold=show` - Make code collapsible but expanded by default
- `code-fold=false` - Show code expanded, not collapsible
**Default behavior:** Code is shown in a collapsed, expandable section. Click to view the code.
## 🔧 How the Filter Works
The `vega-lite.lua` filter:
1. **Detects** code blocks with class `vega-lite`
2. **Includes** Vega libraries (Vega, Vega-Lite, Vega-Embed) from CDN
3. **Renders** the specification using `vegaEmbed()` JavaScript
4. **Handles** code display options (echo, code-fold)
### Libraries Loaded
- `vega@5` - Core Vega library
- `vega-lite@5` - Vega-Lite library
- `vega-embed@6` - Embedding library
## 📝 Migrating from Hugo Shortcodes
**Hugo shortcode (OLD):**
```markdown
{{</* vega-lite id="chart-bar-in-bar" actions="true" */>}}
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"mark": "bar",
...
}
{{</* /vega-lite */>}}
```
**Quarto with Lua filter (NEW):**
````markdown
```{.vega-lite}
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"mark": "bar",
...
}
```
````
**Key differences:**
- No need for `id` attribute - filter auto-generates unique IDs
- No `actions` attribute - actions toolbar enabled by default
- No `load_vega` frontmatter needed - filter includes libraries automatically
- Just paste your Vega-Lite JSON spec inside the code fence!
## 🎯 Migration Steps for Vega Posts
1. **Find Hugo shortcode:**
```markdown
{{</* vega-lite id="..." */>}}
```
2. **Copy the JSON spec** (everything between the shortcode tags)
3. **Replace with code fence:**
````markdown
```{.vega-lite}
{your JSON spec here}
```
````
4. **Remove** the `load_vega: true` from frontmatter (no longer needed)
## 🧪 Testing
After adding a Vega-Lite visualization:
1. **Build:** `quarto render`
2. **Preview:** `quarto preview`
3. **Check:**
- Visualization renders correctly
- Actions toolbar appears (export PNG/SVG/etc.)
- No errors in browser console
- Code display respects your echo/fold settings
## 📋 Example Post with Visualization
```yaml
---
title: "My Vega-Lite Post"
date: "2024-03-15"
lang: en
categories:
- dataviz
- showcase
---
## Simple Bar Chart
```{.vega-lite}
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"description": "A simple bar chart",
"data": {
"values": [
{"category": "A", "value": 28},
{"category": "B", "value": 55},
{"category": "C", "value": 43}
]
},
"mark": "bar",
"encoding": {
"x": {"field": "category", "type": "nominal"},
"y": {"field": "value", "type": "quantitative"}
}
}
```
```
## 🔗 Additional Resources
- [Vega-Lite Documentation](https://vega.github.io/vega-lite/)
- [Vega-Lite Examples](https://vega.github.io/vega-lite/examples/)
- [Quarto Filters Guide](https://quarto.org/docs/extensions/filters.html)
## ⚠️ Troubleshooting
**Visualization not rendering:**
- Check that `vega-lite.lua` exists in the project root
- Verify `filters: - vega-lite.lua` is in `_quarto.yml`
- Ensure JSON spec is valid (test at https://vega.github.io/editor/)
**Browser console errors:**
- Check network tab for CDN library loading issues
- Verify Vega-Lite spec schema version matches (v5)
**Code block shown as plain text:**
- Make sure you're using ````{.vega-lite}` not ````{json}` or ````json`

View File

@@ -1,10 +1,10 @@
<!-- Plausible Analytics -->
<script defer data-domain="olehomelchenko.com" src="https://plausible.io/js/script.file-downloads.outbound-links.tagged-events.js"></script> <script defer data-domain="olehomelchenko.com" src="https://plausible.io/js/script.file-downloads.outbound-links.tagged-events.js"></script>
<script>window.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) }</script> <script>window.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) }</script>
<!-- Matomo --> <!-- Matomo Analytics -->
<script> <script>
var _paq = window._paq = window._paq || []; var _paq = window._paq = window._paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(['trackPageView']); _paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']); _paq.push(['enableLinkTracking']);
(function () { (function () {
@@ -15,19 +15,3 @@
g.async = true; g.src = u + 'matomo.js'; s.parentNode.insertBefore(g, s); g.async = true; g.src = u + 'matomo.js'; s.parentNode.insertBefore(g, s);
})(); })();
</script> </script>
{{ if .Params.load_vega }}
<script src="https://cdn.jsdelivr.net/npm/vega@5"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-lite@5"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-embed@6"></script>
{{ end }}
<style>
.vega-container {
justify-content: center;
display: flex;
}
canvas.marks {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1), 0 6px 20px rgba(0, 0, 0, 0.19);
margin: 10px;
}
</style>
<!-- End Matomo Code -->

3
_includes/fonts.html Normal file
View File

@@ -0,0 +1,3 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Finlandica:ital,wght@0,400..700;1,400..700&family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=IBM+Plex+Sans:ital,wght@0,100..700;1,100..700&display=swap" rel="stylesheet">

69
_quarto.yml Normal file
View File

@@ -0,0 +1,69 @@
project:
type: website
output-dir: _site
resources:
- .nojekyll
render:
- "*.qmd"
- "!archetypes/"
- "!content/"
- "!data/"
- "!i18n/"
- "!layouts/"
- "!public/"
- "!resources/"
- "!static/"
- "!templates/"
- "!themes/"
filters:
- vega-lite.lua
website:
title: "Oleh Omelchenko"
site-url: "https://olehomelchenko.com"
description: "Data visualization and analysis blog"
navbar:
background: light
left:
- text: "Posts"
href: posts
- text: "TILs"
href: til
- text: "Portfolio"
href: portfolio
- text: "Projects"
href: projects
- text: "About"
href: about
right:
- icon: github
href: https://github.com/olehomelchenko
aria-label: GitHub
page-footer:
left: "© 2025 Oleh Omelchenko"
right:
- icon: github
href: https://github.com/olehomelchenko
aria-label: GitHub
format:
html:
theme: custom-theme.scss
css: styles.css
toc: false
page-layout: article
## link-external-newwindow: true
## link-external-icon: false
include-in-header:
- _includes/analytics.html
- _includes/fonts.html
execute:
freeze: auto
echo: true
code-fold: true
code-summary: "Click to show code"

6
_redirects Normal file
View File

@@ -0,0 +1,6 @@
# Redirects for old Hugo URLs (English language paths)
# Redirects old /en/* paths to the new unified structure
/en/posts/* /posts/:splat 301
/en/til/* /til/:splat 301
/en/* /:splat 301

44
about/index.qmd Normal file
View File

@@ -0,0 +1,44 @@
---
title: "About Oleh Omelchenko"
page-layout: article
---
<!-- Replace this template with your actual bio and information -->
### Background
### Skills & Expertise
::: {.grid}
::: {.g-col-12 .g-col-md-4}
**Data Visualization**
- Vega-Lite
- Tableau
- Looker Studio
:::
::: {.g-col-12 .g-col-md-4}
**Analysis & Tools**
- SQL (BigQuery)
- Python (pandas, numpy)
:::
::: {.g-col-12 .g-col-md-4}
**Communication**
- Data Storytelling
:::
:::
### Projects
Check out my [portfolio](/portfolio/) and [projects](/projects/) pages to see examples of my work.
### Contact
- GitHub: [github.com/olehomelchenko](https://github.com/olehomelchenko)
- LinkedIn: [olehomelchenko](https://linkedin.com/in/olehomelchenko)

14
build.sh Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/bash
set -e
# Install Quarto to /tmp (outside repo to avoid processing its template files)
QUARTO_VERSION="1.6.42"
curl -L "https://github.com/quarto-dev/quarto-cli/releases/download/v${QUARTO_VERSION}/quarto-${QUARTO_VERSION}-linux-amd64.tar.gz" -o /tmp/quarto.tar.gz
tar -xzf /tmp/quarto.tar.gz -C /tmp
export PATH="/tmp/quarto-${QUARTO_VERSION}/bin:$PATH"
# Install Python dependencies
pip install -r requirements.txt
# Render site
quarto render

View File

@@ -1,13 +0,0 @@
+++
title = 'Main Page'
date = 2024-05-21T20:20:24+03:00
draft = false
load_vega = true
+++
Привіт! Я Олег, ви можете знати мене як аналітика в MacPaw та/або викладача в Київській школі економіки.
Цей сайт - сховище моїх знахідок в інтернеті, а також подекуди статей за темою аналізу та візуалізації даних.
![](https://static.olehomelchenko.com/oleh-omelchenko.jpeg)

View File

@@ -1,15 +0,0 @@
---
title: Рушій блогу для цього сайту
date: 2024-07-21T20:20:24+03:00
draft: false
---
Для створення цього сайту я надихався декількома блогами.
В першу чергу я зупинився на генераторах статичних сайтів, з міркувань простоти.
З-поміж багатьох існуючих фреймворків обрав один з найбільш популярних (бо по ньому має бути багато напрацювань спільноти) та не закинутий опенсорс проєкт.
В результаті отримав одним з перших кандидатів [Hugo](https://gohugo.io/). Одна з його переваг - широкий вибір тем які можна кастомізувати. Як прихильник мінімальної кількості ганяти лишні байти туди-сюди, обрав тему [PaperMod](https://adityatelange.github.io/hugo-PaperMod/tags/papermod/)
- [Installation](https://github.com/adityatelange/hugo-PaperMod/wiki/Installation)

View File

@@ -1,145 +0,0 @@
---
title: Додавання Vega-Lite візуалізацій в Hugo за допомогою Shortcodes
date: 2024-07-23
draft: false
tags:
- Dataviz
- Hugo
languages:
- Altair
- Vega-Lite
- Hugo
load_vega: true
---
Код специфікації vega-lite, що треба додати:
```json
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"description": "A simple bar chart with embedded data.",
"data": {
"values": [
{"a": "A", "b": 28}, {"a": "B", "b": 55}, {"a": "C", "b": 43},
{"a": "D", "b": 91}, {"a": "E", "b": 81}, {"a": "F", "b": 53},
{"a": "G", "b": 19}, {"a": "H", "b": 87}, {"a": "I", "b": 52}
]
},
"mark": "bar",
"encoding": {
"x": {"field": "a", "title": "aoesuthaosth", "type": "nominal", "axis": {"labelAngle": 0}},
"y": {"field": "b", "type": "quantitative"}
}
}
```
Замість ручного додавання скриптів та ембеддингу як в [туторіалі](https://vega.github.io/vega-lite/usage/embed.html), є можливість підійти трохи гнучкіше і завантажувати vega-lite бібліотеки лише на тих сторінках на яких вони потрібні, а також параметризувати деякі із штук за замовчуванням:
```html
<!-- Create a unique ID for the div where the Vega-Lite visualization will be rendered -->
<div class="vegaVis" id="{{ .Get "id" | default "vega-lite-vis" }}"></div>
<script>
const spec = {{ .Inner | safeJS }};
spec['width'] = spec['width']? spec['width'] : {{ .Get "width" | default 700}};
spec['height'] = spec['height']? spec['height'] : {{ .Get "height" | default 300}};
vegaEmbed('#{{ .Get "id" | default "vega-lite-vis" }}', spec, {"actions": false}).catch(console.error);
</script>
```
Використання (прибрати пробіл до/після `< >`, які я додав щоб воно не рендерилось):
```
{{ < vega-lite id="unique-vis-id-1" actions="true"> }}
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"description": "A simple bar chart with embedded data.",
"data": {
"values": [
{"a": "A", "b": 28}, {"a": "B", "b": 55}, {"a": "C", "b": 43},
{"a": "D", "b": 91}, {"a": "E", "b": 81}, {"a": "F", "b": 53},
{"a": "G", "b": 19}, {"a": "H", "b": 87}, {"a": "I", "b": 52}
]
},
"mark": "bar",
"width": 300,
"encoding": {
"x": {"field": "a", "title": "aoesuthaosth", "type": "nominal", "axis": {"labelAngle": 0}},
"y": {"field": "b", "type": "quantitative"}
}
}
{{ < /vega-lite > }}
```
Результат:
{{< vega-lite id="unique-vis-id-1" >}}
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"description": "A simple bar chart with embedded data.",
"data": {
"values": [
{"a": "A", "b": 28}, {"a": "B", "b": 55}, {"a": "C", "b": 43},
{"a": "D", "b": 91}, {"a": "E", "b": 81}, {"a": "F", "b": 53},
{"a": "G", "b": 19}, {"a": "H", "b": 87}, {"a": "I", "b": 52}
]
},
"mark": "bar",
"width": 300,
"encoding": {
"x": {"field": "a", "title": "aoesuthaosth", "type": "nominal", "axis": {"labelAngle": 0}},
"y": {"field": "b", "type": "quantitative"}
}
}
{{< /vega-lite >}}
{{< vega-lite id="entso-e" dataUrl="https://github.com/olehomelchenko/ua-entso-e-transfers/raw/main/output.json">}}
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"description": "Daily import and export visualization",
"data": {
"url": "https://github.com/olehomelchenko/ua-entso-e-transfers/raw/main/output.json"
},
"transform": [
{ "calculate": "0-datum.Export_from_UA", "as": "Export" },
{ "calculate": "datum.Import_to_UA", "as": "Import" }
],
"width": 800,
"layer": [
{
"mark": "bar",
"encoding": {
"x": { "field": "Date", "type": "temporal", "title": "Date", "timeUnit": "yearmonthdate" },
"y": { "field": "Import", "type": "quantitative", "title": "MW", "aggregate": "sum" },
"color": { "value": "blue" },
"tooltip": [
{ "field": "Date", "type": "temporal", "title": "Date", "timeUnit": "yearmonthdate" },
{ "field": "Import", "type": "quantitative", "title": "Import to UA (MW)", "aggregate": "sum" }
]
}
},
{
"mark": "bar",
"encoding": {
"x": { "field": "Date", "type": "temporal" , "timeUnit": "yearmonthdate"},
"y": { "field": "Export", "type": "quantitative", "aggregate": "sum" },
"color": { "value": "red" },
"tooltip": [
{ "field": "Date", "type": "temporal", "title": "Date", "timeUnit": "yearmonthdate" },
{ "field": "Export", "type": "quantitative", "title": "Export from UA (MW)", "aggregate": "sum" }
]
}
}
]
}
{{< /vega-lite >}}

View File

@@ -1,12 +0,0 @@
---
title: 'Obsidian Paperless'
date: 2024-07-28T02:03:30+03:00
draft: false
---
Серія блогів
https://jamierubin.net/blog-series/practically-paperless-with-obsidian/
Obsidian для воркфлоу наукових досліджень: https://medium.com/@alexandraphelan/an-updated-academic-workflow-zotero-obsidian-cffef080addd

14
custom-theme.scss Normal file
View File

@@ -0,0 +1,14 @@
/*-- scss:defaults --*/
// Import variables FIRST (must come before Bootstrap)
@import "scss/variables";
/*-- scss:rules --*/
// Import our custom modules
@import "scss/colors";
@import "scss/typography";
@import "scss/utilities";
@import "scss/components";
@import "scss/dataviz";
@import "scss/layout";

View File

@@ -1,50 +0,0 @@
baseURL: https://olehomelchenko.com/
title: Олег Омельченко
theme: PaperMod
taxonomies:
category: categories
tag: tags
language: languages
params:
disableThemeToggle: true
defaultTheme: light
env: production
author: Олег Омельченко
defaultContentLanguage: uk
languages:
uk:
disabled: false
languageCode: uk-UA
languageName: Українська
title: Олег Омельченко
weight: 1
en:
disabled: false
languageCode: en-US
languageName: English
params:
author: Oleh Omelchenko
title: Oleh Omelchenko
weight: 2
menu:
main:
- identifier: posts
name: posts
url: /posts/
weight: 50
- identifier: tils
name: TILs
url: /til/
weight: 10
- identifier: categories
name: Categories
url: /categories/
weight: 10
- identifier: tags
name: tags
url: /tags/
weight: 20

34
index.qmd Normal file
View File

@@ -0,0 +1,34 @@
---
title: "Oleh Omelchenko"
page-layout: full
---
My name is Oleh, you may know me as an analyst at MacPaw and/or a teacher at Kyiv School of Economics.
This website is a stash of my findings on the web, as well as some articles mostly related to data analysis and visualization.
![](https://static.olehomelchenko.com/oleh-omelchenko.jpeg){width=300}
### Recent Posts
::: {.grid}
::: {.g-col-12 .g-col-md-4}
### [Posts](/posts/)
Latest articles and analysis
:::
::: {.g-col-12 .g-col-md-4}
### [Portfolio](/portfolio/)
Selected visualization work
:::
::: {.g-col-12 .g-col-md-4}
### [TILs](/til/)
Quick tips and learnings
:::
:::
---
Explore my [projects](/projects/) or learn more [about me](/about/).

View File

@@ -1,16 +0,0 @@
<!-- Create a unique ID for the div where the Vega-Lite visualization will be rendered -->
<div class="vega-container">
<div class="vegaVis" id="{{ .Get "id" | default "vega-lite-vis" }}"></div>
</div>
<script>
(function() {
let spec = {{ .Inner | safeJS }};
let actions = {{ .Get "actions" | default false}};
spec['width'] = spec['width']? spec['width'] : {{ .Get "width" | default 700}};
spec['height'] = spec['height']? spec['height'] : {{ .Get "height" | default 300}};
vegaEmbed('#{{ .Get "id" | default "vega-lite-vis" }}', spec, {"actions": actions}).catch(console.error);
})();
</script>

26
portfolio/index.qmd Normal file
View File

@@ -0,0 +1,26 @@
---
title: "Portfolio"
page-layout: full
---
Selected data visualization projects showcasing interactive charts, dashboards, and analytical narratives.
::: {.grid}
::: {.g-col-12 .g-col-md-6}
### [Sample Visualization Post](../posts/sample-visualization-post/)
Example post with Vega-Lite visualization - posts with the `showcase` category will appear here.
:::
:::
---
**Note:** After migrating your Hugo posts, manually add links here to your visualization-heavy posts (those with the `showcase` category). Example:
```markdown
::: {.g-col-12 .g-col-md-6}
### [Forbes Top-50 Foundations](../posts/forbes-50-foundations/)
Interactive visualization of Ukraine's top charitable foundations
:::
```

View File

@@ -1,17 +1,14 @@
--- ---
title: "Bar Charts: Makeover in Vega-Lite" title: "Bar Charts: Makeover in Vega-Lite"
date: 2025-07-07 date: 2025-07-07
draft: false image: https://static.olehomelchenko.com/blog/tableau-seven-bar-charts-yoy-growth.png
load_vega: true
tags: tags:
- dataviz - dataviz
- vega-lite - vega-lite
- makeover - makeover
--- ---
## oeuth
Of all tools for data visualization, I mostly enjoy working with Vega-Lite. As I enhance my skills in making the visualizations using it, I become increasingly interesting not just in the "dataviz" part of its capabilities, but also on making it aesthetically compelling, as well as challenging myself to reach the limits of what is possible with Vega-Lite. I am by no means near the limits so far, however I got increasingly interested in replicating the charts I see throughout the internet. Of all tools for data visualization, I mostly enjoy working with Vega-Lite. As I enhance my skills in making the visualizations using it, I become increasingly interesting not just in the "dataviz" part of its capabilities, but also on making it aesthetically compelling, as well as challenging myself to reach the limits of what is possible with Vega-Lite. I am by no means near the limits so far, however I got increasingly interested in replicating the charts I see throughout the internet.
One that caught me attention is Tableau's Viz of the Day: [Seven Bar Charts to Visualise Year-Over-Year Growth](https://public.tableau.com/app/profile/jacob.rothemund/viz/SevenBarChartstoVisualiseYear-Over-YearGrowth/Dashboard) One that caught me attention is Tableau's Viz of the Day: [Seven Bar Charts to Visualise Year-Over-Year Growth](https://public.tableau.com/app/profile/jacob.rothemund/viz/SevenBarChartstoVisualiseYear-Over-YearGrowth/Dashboard)
@@ -75,7 +72,7 @@ After this, we can make the following layers:
- `text` mark with calculated `text_label` - `text` mark with calculated `text_label`
- two separate `point` mark layers, each with a different color but more importantly - `triangle-up` and `triangle-down`. We cannot encode the two marks dynamically (or, rather, we cannot make dynamic fill color which is my goal here) so we'll need to do it separately for positive and negative changes. - two separate `point` mark layers, each with a different color but more importantly - `triangle-up` and `triangle-down`. We cannot encode the two marks dynamically (or, rather, we cannot make dynamic fill color which is my goal here) so we'll need to do it separately for positive and negative changes.
{{< vega-lite id="chart-bar-in-bar" actions="true">}} ```{.vega-lite}
{ {
"$schema": "https://vega.github.io/schema/vega-lite/v5.json", "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"width": 400, "width": 400,
@@ -300,13 +297,13 @@ After this, we can make the following layers:
} }
] ]
} }
{{< /vega-lite >}} ```
## Highlighted Absolute Change ## Highlighted Absolute Change
This one largely repeats the bar-in-bar, with the exception that the highlighted changes are achieved by combining `x` and `x2` channels combined with `color` This one largely repeats the bar-in-bar, with the exception that the highlighted changes are achieved by combining `x` and `x2` channels combined with `color`
{{< vega-lite id="chart-highlighted-absolute-change" actions="true">}} ```{.vega-lite}
{ {
"$schema": "https://vega.github.io/schema/vega-lite/v5.json", "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"width": 400, "width": 400,
@@ -556,13 +553,13 @@ This one largely repeats the bar-in-bar, with the exception that the highlighted
} }
] ]
} }
{{< /vega-lite >}} ```
## Bullet ## Bullet
Key difference in schema here - instead of highlighted bar mark, we use `tick` with exact value for the previous period. Key difference in schema here - instead of highlighted bar mark, we use `tick` with exact value for the previous period.
{{< vega-lite id="chart-bullet" actions="true">}} ```{.vega-lite}
{ {
"$schema": "https://vega.github.io/schema/vega-lite/v5.json", "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"width": 400, "width": 400,
@@ -793,13 +790,13 @@ Key difference in schema here - instead of highlighted bar mark, we use `tick` w
} }
] ]
} }
{{< /vega-lite >}} ```
## Direction Arrows ## Direction Arrows
{{< vega-lite id="chart-direction-arrows" actions="true">}} ```{.vega-lite}
{ {
"$schema": "https://vega.github.io/schema/vega-lite/v5.json", "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"width": 400, "width": 400,
@@ -1044,7 +1041,8 @@ Key difference in schema here - instead of highlighted bar mark, we use `tick` w
} }
] ]
} }
{{< /vega-lite >}} ```
## Error Bars ## Error Bars

View File

@@ -2,8 +2,10 @@
title: Блоги та Ресурси з візуалізації даних title: Блоги та Ресурси з візуалізації даних
date: 2024-07-20 date: 2024-07-20
draft: false draft: false
lang: uk
tags: tags:
- Датавіз - Датавіз
- Dataviz
--- ---
- https://datatowel.in.ua/ - демографія, економіка, електоральна географія України - https://datatowel.in.ua/ - демографія, економіка, електоральна географія України

View File

@@ -8,6 +8,7 @@ languages:
- R - R
- Python - Python
--- ---
### Аналіз даних ### Аналіз даних
- **The Data Journalism Handbook**: - **The Data Journalism Handbook**:
- [переклад українською на texty.org.ua (2015)](https://texty.org.ua/archive-books/40161/zhurnalistyka-danykh-posibnyk-40161/), - [переклад українською на texty.org.ua (2015)](https://texty.org.ua/archive-books/40161/zhurnalistyka-danykh-posibnyk-40161/),
@@ -18,20 +19,24 @@ languages:
- The Quartz guide to bad data - [Оригінал EN (GitHub)](https://github.com/Quartz/bad-data-guide) - The Quartz guide to bad data - [Оригінал EN (GitHub)](https://github.com/Quartz/bad-data-guide)
### Візуалізація даних ### Візуалізація даних
- **Visualization Analysis and Design*- by Tamara Munzner: [taylorfrancis.com](https://www.taylorfrancis.com/books/mono/10.1201/b17511/visualization-analysis-design-tamara-munzner) - **Visualization Analysis and Design**- by Tamara Munzner: [taylorfrancis.com](https://www.taylorfrancis.com/books/mono/10.1201/b17511/visualization-analysis-design-tamara-munzner)
- **Fundamentals of Data Visualization*- by Claus O. Wilke: [clauswilke.com/dataviz](https://clauswilke.com/dataviz/) - **Fundamentals of Data Visualization**- by Claus O. Wilke: [clauswilke.com/dataviz](https://clauswilke.com/dataviz/)
- **Good Charts: The HBR Guide to Making Smarter, More Persuasive Data Visualizations** - **Good Charts: The HBR Guide to Making Smarter, More Persuasive Data Visualizations**
- EN: [Amazon](https://www.amazon.com/dp/1633690709) - EN: [Amazon](https://www.amazon.com/dp/1633690709)
- **Good Charts Workbook: Tips, Tools, and Exercises for Making Better Data Visualizations** - **Good Charts Workbook: Tips, Tools, and Exercises for Making Better Data Visualizations**
- EN: [Amazon](https://www.amazon.com/dp/1633696170) - EN: [Amazon](https://www.amazon.com/dp/1633696170)
- Українською: [ArtHuss](https://www.arthuss.com.ua/shop/khoroshi-diahramy) - Українською: [ArtHuss](https://www.arthuss.com.ua/shop/khoroshi-diahramy)
- **ggplot2 book*- by Hadley Wickham: [ggplot2-book.org](https://ggplot2-book.org/) - **Storytelling With Data** by Cole N. Knaflic
- EN: [Amazon](https://www.amazon.com/Storytelling-Data-Visualization-Business-Professionals/dp/1119002257?linkId=309f73640a0cb64e3b676e26561d07a0&language=en_US)
- Українською: [ArtHuss](https://www.arthuss.com.ua/shop/efektyvna-rozpovid-za-dopomohoyu-danykh)
- **ggplot2 book**- by Hadley Wickham: [ggplot2-book.org](https://ggplot2-book.org/)
### Курси та навчальні програми ### Курси та навчальні програми
- **SDS 375 - Data Visualization in R*- - by Claus O. Wilke: https://wilkelab.org/SDS375/ - **SDS 375 - Data Visualization in R**- - by Claus O. Wilke: https://wilkelab.org/SDS375/
- **Computing for Information Science*- by Benjamin Soltoff: https://info5940.infosci.cornell.edu/ - **DS 4200 F23: Information Presentation & Visualization** - https://neu-ds-4200-f23.github.io/
- **Computing for Information Science**- by Benjamin Soltoff: https://info5940.infosci.cornell.edu/
### Інше ### Інше
- **A Layered Grammar of Graphics*- by [Hadley Wickham](https://substack.com/@hadleywickham): [PDF](https://static.olehomelchenko.com/wickham_layered-grammar.pdf) - **A Layered Grammar of Graphics**- by [Hadley Wickham](https://substack.com/@hadleywickham): [PDF](https://static.olehomelchenko.com/wickham_layered-grammar.pdf)
- **Science of Visual Data Communication - What Works** [Online](https://journals.sagepub.com/doi/epub/10.1177/15291006211051956)

View File

@@ -1,14 +1,10 @@
--- ---
title: Візуалізація руху електроенергії з та в Україну за даними ENTSO-E title: Візуалізація руху електроенергії з та в Україну за даними ENTSO-E
date: 2024-07-21T20:20:24+03:00 date: 2024-07-21T20:20:24+03:00
draft: true image: https://static.olehomelchenko.com/2026-01-10%2004.45.50.png
load_vega: true
--- ---
```{.vega-lite}
{{< vega-lite id="entso-e-bar" dataUrl="https://github.com/olehomelchenko/ua-entso-e-transfers/raw/main/output.json">}}
{ {
"$schema": "https://vega.github.io/schema/vega-lite/v5.json", "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": { "data": {
@@ -130,11 +126,11 @@ load_vega: true
} }
} }
} }
{{< /vega-lite >}} ```
{{< vega-lite id="entso-e-dynamics" dataUrl="https://github.com/olehomelchenko/ua-entso-e-transfers/raw/main/output.json">}} ```{.vega-lite}
{ {
"$schema": "https://vega.github.io/schema/vega-lite/v5.json", "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": { "data": {
@@ -323,4 +319,4 @@ load_vega: true
} }
] ]
} }
{{< /vega-lite >}} ```

View File

@@ -2,6 +2,8 @@
title: "Візуалізація: Топ-50 благодійних фондів за версією Forbes" title: "Візуалізація: Топ-50 благодійних фондів за версією Forbes"
date: 2024-09-11 date: 2024-09-11
draft: false draft: false
lang: uk
image: https://static.olehomelchenko.com/2026-01-08%2005.32.39.png
tags: tags:
- Dataviz - Dataviz
- Showcase - Showcase
@@ -9,7 +11,6 @@ languages:
- Vega-Lite - Vega-Lite
load_vega: true
--- ---
В нещодавньому випуску український Forbes опублікував список топ-50 благодійних фондів України. В нещодавньому випуску український Forbes опублікував список топ-50 благодійних фондів України.
@@ -23,7 +24,7 @@ load_vega: true
## Список фондів за структурою надходжень ## Список фондів за структурою надходжень
{{< vega-lite id="entso-e-dynamics" width=600 height=1000 >}} ```{.vega-lite}
{ {
"$schema": "https://vega.github.io/schema/vega-lite/v5.json", "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"config": { "config": {
@@ -51,7 +52,7 @@ load_vega: true
"anchor": "middle" "anchor": "middle"
} }
}, },
"orient": "bottom", "orient": "top",
"gradientLength": 500, "gradientLength": 500,
"labelLimit": 500, "labelLimit": 500,
"columns": 3, "columns": 3,
@@ -81,7 +82,7 @@ load_vega: true
"width": 600, "width": 600,
"params": [ "params": [
{ {
"name": "org", "name": "Джерело",
"select": {"type": "point", "fields": ["Джерело"]}, "select": {"type": "point", "fields": ["Джерело"]},
"bind": { "bind": {
"input": "select", "input": "select",
@@ -115,7 +116,7 @@ load_vega: true
"fold": ["Юрособи", "Фізособи", "Нерезиденти", "Інше"], "fold": ["Юрособи", "Фізособи", "Нерезиденти", "Інше"],
"as": ["Джерело", "value"] "as": ["Джерело", "value"]
}, },
{"filter": {"param": "org"}} {"filter": {"param": "Джерело"}}
], ],
"data": { "data": {
"values": [ "values": [
@@ -922,4 +923,4 @@ load_vega: true
] ]
} }
} }
{{< /vega-lite >}} ```

16
posts/index.qmd Normal file
View File

@@ -0,0 +1,16 @@
---
title: "Posts"
page-layout: full
listing:
contents: "*.qmd"
type: grid
sort: "date desc"
categories: true
sort-ui: true
filter-ui: true
fields: [image, date, title, reading-time, categories]
date-format: "D MMMM YYYY"
page-size: 10
---
All blog posts covering data visualization, analysis, and related topics. Posts are available in both Ukrainian and English.

30
projects/index.qmd Normal file
View File

@@ -0,0 +1,30 @@
---
title: "Projects"
page-layout: full
---
## Featured Projects
::: {.grid}
::: {.g-col-12 .g-col-md-6}
### [Sample Project 1](sample-project-1/)
A full project page with detailed analysis and visualizations. Replace with your actual projects.
:::
::: {.g-col-12 .g-col-md-6}
### Sample Project 2
A brief description without a dedicated page. Just a reference in the listing.
:::
:::
---
## Other Projects
- **Quick Project Reference 1** - Brief description
- **Quick Project Reference 2** - Brief description
- **Quick Project Reference 3** - Brief description
Some projects get full dedicated pages (like Sample Project 1 above), while others are just listed here with brief descriptions.

View File

@@ -0,0 +1,32 @@
---
title: "Sample Project 1"
subtitle: "Full Project Page Example"
date: "2024-03-01"
categories:
- project
- dataviz
---
## Overview
This is an example of a full project page. Some projects warrant detailed explanations with multiple sections, code examples, and visualizations.
### Background
Describe the context and motivation for this project.
### Approach
Explain your methodology and approach.
### Results
Show your findings, visualizations, and analysis.
### Conclusions
Summarize the key takeaways.
---
Replace this with your actual project content.

View File

@@ -1,38 +0,0 @@
# Olehomelchenko.com
## Installation
1. Clone the repository:
```sh
git clone https://github.com/olehomelchenko/olehomelchenko.com.git
cd olehomelchenko.com
```
2. Install Hugo:
```sh
brew install hugo
```
## Development
To start the development server, run:
```sh
hugo server
```
This will start the server at `http://localhost:1313`.
## Build
To build the project for production, run:
```sh
hugo
```
The output will be in the `public` directory.
## Create New Content
To create new content, use the following command:
```sh
hugo new posts/your-new-post.md
```
This will create a new markdown file in the `content/posts` directory.

1
requirements.txt Normal file
View File

@@ -0,0 +1 @@
jupyter==1.1.1

44
scss/_colors.scss Normal file
View File

@@ -0,0 +1,44 @@
:root {
// Brand Colors
--color-primary-blue: #{$primary-blue};
--color-primary-yellow: #{$primary-yellow};
--color-text: #{$text-color};
--color-background: #{$background-color};
// Semantic colors
--text: #2c2a26;
--background: #f5f3f0;
--primary: #296eb3;
--secondary: #fdba35;
--accent: #188afb;
// UI Elements
--link-color: #{$primary-blue};
--link-hover-color: #{darken($primary-blue, 10%)};
// Data Visualization Colors
--dataviz-cat-1: #{$dataviz-cat-1};
--dataviz-cat-2: #{$dataviz-cat-2};
--dataviz-cat-3: #{$dataviz-cat-3};
--dataviz-cat-4: #{$dataviz-cat-4};
--dataviz-cat-5: #{$dataviz-cat-5};
--dataviz-cat-6: #{$dataviz-cat-6};
--dataviz-cat-7: #{$dataviz-cat-7};
--dataviz-cat-8: #{$dataviz-cat-8};
}
// Apply colors
body {
background-color: var(--color-background);
color: var(--color-text);
}
a {
color: var(--link-color);
text-decoration: none;
&:hover {
color: var(--link-hover-color);
text-decoration: underline;
}
}

100
scss/_components.scss Normal file
View File

@@ -0,0 +1,100 @@
// Navbar styling
.navbar {
background-color: var(--color-background) !important;
border-bottom: 2px solid var(--color-text);
.navbar-brand {
color: var(--color-text) !important;
font-family: $headings-font-family;
font-weight: 600;
}
.navbar-nav .nav-link {
color: var(--color-text) !important;
&:hover {
color: var(--color-primary-blue) !important;
}
&.active {
color: var(--color-primary-blue) !important;
}
}
}
// Footer
.nav-footer {
background-color: var(--color-background);
border-top: 2px solid var(--color-text);
color: var(--color-text);
a {
color: var(--color-primary-blue);
}
}
// Buttons
.btn-primary {
background-color: var(--color-primary-blue);
border-color: var(--color-primary-blue);
color: white;
&:hover {
background-color: #{darken($primary-blue, 10%)};
border-color: #{darken($primary-blue, 10%)};
}
}
.btn-secondary {
background-color: var(--color-primary-yellow);
border-color: var(--color-primary-yellow);
color: var(--color-text);
&:hover {
background-color: #{darken($primary-yellow, 10%)};
border-color: #{darken($primary-yellow, 10%)};
}
}
// Code blocks
pre.sourceCode {
background-color: rgba(45, 42, 38, 0.05);
border: 1px solid rgba(45, 42, 38, 0.1);
padding: 1em;
}
code.sourceCode {
color: var(--color-text);
}
// Quarto title block
.quarto-title-block {
border-bottom: 2px solid var(--color-text);
margin-bottom: 2rem;
.quarto-title .title {
font-family: $headings-font-family;
color: var(--color-text);
}
}
// Listing pages
.quarto-listing-default {
.listing-item {
border: 1px solid rgba(45, 42, 38, 0.2);
padding: 1rem;
margin-bottom: 1rem;
&:hover {
border-color: var(--color-primary-blue);
}
}
.listing-title a {
color: var(--color-text);
&:hover {
color: var(--color-primary-blue);
}
}
}

53
scss/_dataviz.scss Normal file
View File

@@ -0,0 +1,53 @@
// Sequential color ramps
// Blue sequential (5 steps from light to dark)
:root {
--dataviz-blue-seq-1: #e6f2ff;
--dataviz-blue-seq-2: #9bccfd;
--dataviz-blue-seq-3: #3699fc;
--dataviz-blue-seq-4: #047ffb;
--dataviz-blue-seq-5: #024c97;
// Yellow sequential (5 steps)
--dataviz-yellow-seq-1: #fff6e6;
--dataviz-yellow-seq-2: #fedd9a;
--dataviz-yellow-seq-3: #fdba35;
--dataviz-yellow-seq-4: #ca8702;
--dataviz-yellow-seq-5: #654401;
// Diverging scale (blue ↔ neutral ↔ yellow)
--dataviz-diverging-1: #296EB4;
--dataviz-diverging-2: #9FDFDC;
--dataviz-diverging-3: #f5f3f0;
--dataviz-diverging-4: #FDB833;
--dataviz-diverging-5: #fca903;
}
// Categorical color utility classes (for future use)
.dataviz-cat-1 { color: $dataviz-cat-1; }
.dataviz-cat-2 { color: $dataviz-cat-2; }
.dataviz-cat-3 { color: $dataviz-cat-3; }
.dataviz-cat-4 { color: $dataviz-cat-4; }
.dataviz-cat-5 { color: $dataviz-cat-5; }
.dataviz-cat-6 { color: $dataviz-cat-6; }
.dataviz-cat-7 { color: $dataviz-cat-7; }
.dataviz-cat-8 { color: $dataviz-cat-8; }
.dataviz-cat-bg-1 { background-color: $dataviz-cat-1; }
.dataviz-cat-bg-2 { background-color: $dataviz-cat-2; }
.dataviz-cat-bg-3 { background-color: $dataviz-cat-3; }
.dataviz-cat-bg-4 { background-color: $dataviz-cat-4; }
.dataviz-cat-bg-5 { background-color: $dataviz-cat-5; }
.dataviz-cat-bg-6 { background-color: $dataviz-cat-6; }
.dataviz-cat-bg-7 { background-color: $dataviz-cat-7; }
.dataviz-cat-bg-8 { background-color: $dataviz-cat-8; }
/*
* Data Visualization Color System
*
* Categorical (8 colors): Use .dataviz-cat-1 through .dataviz-cat-8
* Sequential Blue: var(--dataviz-blue-seq-1) through var(--dataviz-blue-seq-5)
* Sequential Yellow: var(--dataviz-yellow-seq-1) through var(--dataviz-yellow-seq-5)
* Diverging: var(--dataviz-diverging-1) through var(--dataviz-diverging-5)
*
* All colors are colorblind-safe as per brand guidelines.
*/

2
scss/_layout.scss Normal file
View File

@@ -0,0 +1,2 @@
// Layout-specific styles
// Initially empty - for future layout customizations

31
scss/_typography.scss Normal file
View File

@@ -0,0 +1,31 @@
// Body text
body {
font-family: $font-family-sans-serif;
font-optical-sizing: auto;
}
// Headings
h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
font-family: $headings-font-family;
font-optical-sizing: auto;
color: var(--color-text);
}
// Code blocks
code, pre, kbd, samp {
font-family: $font-family-monospace;
}
// Quarto-specific code blocks
.sourceCode {
font-family: $font-family-monospace;
}
// Inline code
p code, li code {
font-family: $font-family-monospace;
background-color: rgba(45, 42, 38, 0.05);
padding: 0.2em 0.4em;
color: var(--color-text);
}

36
scss/_utilities.scss Normal file
View File

@@ -0,0 +1,36 @@
// Global border-radius override (sharp corners principle)
* {
border-radius: 0 !important;
}
// Remove any gradients from buttons
.btn {
background-image: none !important;
border-radius: 0 !important;
}
// Navbar - sharp corners, solid colors
.navbar {
border-radius: 0 !important;
background-image: none !important;
}
// Cards and panels
.card {
border-radius: 0 !important;
}
// Code blocks
pre, code {
border-radius: 0 !important;
}
// Quarto-specific elements
.quarto-title-block {
border-radius: 0 !important;
}
// Input elements
input, select, textarea, button {
border-radius: 0 !important;
}

33
scss/_variables.scss Normal file
View File

@@ -0,0 +1,33 @@
// Brand Colors
$primary-blue: #1789fc;
$primary-yellow: #fdb833;
$text-color: #2d2a26;
$background-color: #f5f3f0;
// Bootstrap/Quarto Variable Overrides
$primary: $primary-blue;
$secondary: $primary-yellow;
$body-bg: $background-color;
$body-color: $text-color;
// Typography
$font-family-sans-serif: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
$font-family-monospace: "IBM Plex Mono", "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
$headings-font-family: "Finlandica", "IBM Plex Sans", sans-serif;
// Design Principles
$border-radius: 0;
$border-radius-sm: 0;
$border-radius-lg: 0;
$enable-rounded: false;
$enable-gradients: false;
// Data Viz Categorical Colors
$dataviz-cat-1: #296EB4;
$dataviz-cat-2: #FDB833;
$dataviz-cat-3: #CCE160;
$dataviz-cat-4: #9FDFDC;
$dataviz-cat-5: #8A983E;
$dataviz-cat-6: #C7C7FA;
$dataviz-cat-7: #D02F2F;
$dataviz-cat-8: #A62191;

74
styles.css Normal file
View File

@@ -0,0 +1,74 @@
/* Minimal custom styling for olehomelchenko.com */
/* Vega-Lite visualization container */
.vega-viz-container {
margin: 20px 0;
}
/* Reverse order to put filters on top of canvas */
.chart-wrapper {
display: flex;
flex-direction: column-reverse;
align-items: center;
gap: 20px;
}
/* Vega-Lite visualization styling - center and add subtle shadow */
.vega-embed {
justify-content: center;
display: flex;
}
.vega-embed canvas.marks {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
margin: 10px;
}
/* Style the interactive parameter filters */
/* Hide when empty, style only when it has content */
.vega-bindings:empty {
display: none;
}
.vega-bindings:not(:empty) {
background: var(--color-background, #f5f3f0);
border: 1px solid rgba(45, 42, 38, 0.2);
border-radius: 0;
padding: 15px 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
width: fit-content;
}
.vega-bindings label {
display: inline-flex;
align-items: center;
gap: 10px;
margin-right: 20px;
font-family: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
font-size: 14px;
color: var(--color-text, #2d2a26);
font-weight: 500;
}
.vega-bindings input,
.vega-bindings select {
padding: 6px 12px;
border: 1px solid rgba(45, 42, 38, 0.2);
border-radius: 0;
background: white;
font-size: 14px;
color: var(--color-text, #2d2a26);
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
.vega-bindings input:focus,
.vega-bindings select:focus {
border-color: var(--color-primary-blue, #1789fc);
outline: 0;
box-shadow: 0 0 0 0.2rem rgba(23, 137, 252, 0.25);
}
.vega-bindings select {
cursor: pointer;
min-width: 150px;
}

313
themes-guide.md Normal file
View File

@@ -0,0 +1,313 @@
## Personal Design System
### Brand Colors
| Role | Hex | Usage |
|------|-----|-------|
| Primary Blue | `#1789fc` | UI accents, links, interactive elements |
| Primary Yellow | `#fdb833` | Highlights, secondary accents |
| Text | `#2d2a26` | Body text, headings |
| Background | `#f5f3f0` | Page background |
### Data Visualization
**Categorical (8 colors):**
`#296EB4` `#FDB833` `#CCE160` `#9FDFDC` `#8A983E` `#C7C7FA` `#D02F2F` `#A62191`
**Sequential:** Ramps derived from `#1789fc` (blue) or `#fdb833` (yellow)
**Diverging:** Blue ↔ neutral midpoint ↔ Yellow
### Typography
| Role | Font |
|------|------|
| Headings | Finlandica |
| Body | IBM Plex Sans |
| Code | IBM Plex Mono |
### Design Principles
- Sharp corners, no border-radius
- Solid fills over gradients
- Light mode only
- Colorblind-safe palette
## Font instructions from Google Fonts
Code below was generated by Google Fonts:
```
Embed code in the <head> of your html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Finlandica:ital,wght@0,400..700;1,400..700&family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=IBM+Plex+Sans:ital,wght@0,100..700;1,100..700&display=swap" rel="stylesheet">
IBM Plex Sans: CSS class for a variable style
.ibm-plex-sans-<uniquifier> {
font-family: "IBM Plex Sans", sans-serif;
font-optical-sizing: auto;
font-weight: <weight>;
font-style: normal;
font-variation-settings:
"wdth" 100;
}
Finlandica: CSS class for a variable style
.finlandica-<uniquifier> {
font-family: "Finlandica", sans-serif;
font-optical-sizing: auto;
font-weight: <weight>;
font-style: normal;
}
IBM Plex Mono: CSS classes
.ibm-plex-mono-thin {
font-family: "IBM Plex Mono", monospace;
font-weight: 100;
font-style: normal;
}
.ibm-plex-mono-extralight {
font-family: "IBM Plex Mono", monospace;
font-weight: 200;
font-style: normal;
}
.ibm-plex-mono-light {
font-family: "IBM Plex Mono", monospace;
font-weight: 300;
font-style: normal;
}
.ibm-plex-mono-regular {
font-family: "IBM Plex Mono", monospace;
font-weight: 400;
font-style: normal;
}
.ibm-plex-mono-medium {
font-family: "IBM Plex Mono", monospace;
font-weight: 500;
font-style: normal;
}
.ibm-plex-mono-semibold {
font-family: "IBM Plex Mono", monospace;
font-weight: 600;
font-style: normal;
}
.ibm-plex-mono-bold {
font-family: "IBM Plex Mono", monospace;
font-weight: 700;
font-style: normal;
}
.ibm-plex-mono-thin-italic {
font-family: "IBM Plex Mono", monospace;
font-weight: 100;
font-style: italic;
}
.ibm-plex-mono-extralight-italic {
font-family: "IBM Plex Mono", monospace;
font-weight: 200;
font-style: italic;
}
.ibm-plex-mono-light-italic {
font-family: "IBM Plex Mono", monospace;
font-weight: 300;
font-style: italic;
}
.ibm-plex-mono-regular-italic {
font-family: "IBM Plex Mono", monospace;
font-weight: 400;
font-style: italic;
}
.ibm-plex-mono-medium-italic {
font-family: "IBM Plex Mono", monospace;
font-weight: 500;
font-style: italic;
}
.ibm-plex-mono-semibold-italic {
font-family: "IBM Plex Mono", monospace;
font-weight: 600;
font-style: italic;
}
.ibm-plex-mono-bold-italic {
font-family: "IBM Plex Mono", monospace;
font-weight: 700;
font-style: italic;
}
```
## Colors for website theme
The code below was generated by Realtime Colors website.
General variables:
```
:root[data-theme="light"] {
--text: #2c2a26;
--background: #f5f3f0;
--primary: #296eb3;
--secondary: #fdba35;
--accent: #188afb;
}
:root[data-theme="dark"] {
--text: #d9d7d3;
--background: #0f0d0a;
--primary: #4c91d6;
--secondary: #ca8702;
--accent: #0475e7;
}
```
Option with shades:
```
:root[data-theme="light"] {
--text-50: #f3f3f1;
--text-100: #e7e6e4;
--text-200: #d0cdc8;
--text-300: #b8b4ad;
--text-400: #a09b92;
--text-500: #888277;
--text-600: #6d685f;
--text-700: #524e47;
--text-800: #37342f;
--text-900: #1b1a18;
--text-950: #0e0d0c;
--background-50: #f5f3f0;
--background-100: #ebe7e0;
--background-200: #d6cec2;
--background-300: #c2b6a3;
--background-400: #ad9d85;
--background-500: #998566;
--background-600: #7a6a52;
--background-700: #5c503d;
--background-800: #3d3529;
--background-900: #1f1b14;
--background-950: #0f0d0a;
--primary-50: #eaf2fa;
--primary-100: #d5e5f6;
--primary-200: #acccec;
--primary-300: #82b2e3;
--primary-400: #5999d9;
--primary-500: #2f7fd0;
--primary-600: #2666a6;
--primary-700: #1c4c7d;
--primary-800: #133353;
--primary-900: #09192a;
--primary-950: #050d15;
--secondary-50: #fff6e6;
--secondary-100: #feeecd;
--secondary-200: #fedd9a;
--secondary-300: #fdcb68;
--secondary-400: #fdba35;
--secondary-500: #fca903;
--secondary-600: #ca8702;
--secondary-700: #976502;
--secondary-800: #654401;
--secondary-900: #322201;
--secondary-950: #191100;
--accent-50: #e6f2ff;
--accent-100: #cde5fe;
--accent-200: #9bccfd;
--accent-300: #68b2fd;
--accent-400: #3699fc;
--accent-500: #047ffb;
--accent-600: #0366c9;
--accent-700: #024c97;
--accent-800: #023364;
--accent-900: #011932;
--accent-950: #000d19;
}
:root[data-theme="dark"] {
--text-50: #0e0d0c;
--text-100: #1b1a18;
--text-200: #37342f;
--text-300: #524e47;
--text-400: #6d685f;
--text-500: #888277;
--text-600: #a09b92;
--text-700: #b8b4ad;
--text-800: #d0cdc8;
--text-900: #e7e6e4;
--text-950: #f3f3f1;
--background-50: #0f0d0a;
--background-100: #1f1b14;
--background-200: #3d3529;
--background-300: #5c503d;
--background-400: #7a6a52;
--background-500: #998566;
--background-600: #ad9d85;
--background-700: #c2b6a3;
--background-800: #d6cec2;
--background-900: #ebe7e0;
--background-950: #f5f3f0;
--primary-50: #050d15;
--primary-100: #09192a;
--primary-200: #133353;
--primary-300: #1c4c7d;
--primary-400: #2666a6;
--primary-500: #2f7fd0;
--primary-600: #5999d9;
--primary-700: #82b2e3;
--primary-800: #acccec;
--primary-900: #d5e5f6;
--primary-950: #eaf2fa;
--secondary-50: #191100;
--secondary-100: #322201;
--secondary-200: #654401;
--secondary-300: #976502;
--secondary-400: #ca8702;
--secondary-500: #fca903;
--secondary-600: #fdba35;
--secondary-700: #fdcb68;
--secondary-800: #fedd9a;
--secondary-900: #feeecd;
--secondary-950: #fff6e6;
--accent-50: #000d19;
--accent-100: #011932;
--accent-200: #023364;
--accent-300: #024c97;
--accent-400: #0366c9;
--accent-500: #047ffb;
--accent-600: #3699fc;
--accent-700: #68b2fd;
--accent-800: #9bccfd;
--accent-900: #cde5fe;
--accent-950: #e6f2ff;
}
```

Submodule themes/PaperMod deleted from 5a4651783f

2
til/_metadata.yml Normal file
View File

@@ -0,0 +1,2 @@
# Shared metadata for TILs (Today I Learned)
freeze: auto

14
til/index.qmd Normal file
View File

@@ -0,0 +1,14 @@
---
title: "TILs - Today I Learned"
page-layout: full
listing:
contents: "*.qmd"
type: default
sort: "date desc"
fields: [date, title, categories]
date-format: "YYYY-MM-DD"
# table-striped: true
# filter-ui: true
---
Quick tips, discoveries, and learnings. Short-form content about interesting things I've discovered.

View File

@@ -0,0 +1,12 @@
---
title: 'Obsidian Paperless'
date: 2024-07-28T02:03:30+03:00
draft: false
---
Серія блогів
[https://jamierubin.net/blog-series/practically-paperless-with-obsidian/](https://jamierubin.net/blog-series/practically-paperless-with-obsidian/ )
Obsidian для воркфлоу наукових досліджень: [https://medium.com/@alexandraphelan/an-updated-academic-workflow-zotero-obsidian-cffef080addd](https://medium.com/@alexandraphelan/an-updated-academic-workflow-zotero-obsidian-cffef080addd)

28
til/ojs-in-quarto.qmd Normal file
View File

@@ -0,0 +1,28 @@
---
title: ObservableJS in Quarto
date: 2025-01-10
draft: false
---
```{ojs}
vegalite = require("@observablehq/vega-lite")
vegalite({
"$schema": "https://vega.github.io/schema/vega-lite/v6.json",
"description": "A simple bar chart with embedded data.",
"data": {
"values": [
{"a": "A", "b": 28}, {"a": "B", "b": 55}, {"a": "C", "b": 43},
{"a": "D", "b": 91}, {"a": "E", "b": 81}, {"a": "F", "b": 53},
{"a": "G", "b": 19}, {"a": "H", "b": 87}, {"a": "I", "b": 52}
]
},
"mark": "bar",
"encoding": {
"x": {"field": "a", "type": "nominal", "axis": {"labelAngle": 0}},
"y": {"field": "b", "type": "quantitative"}
}
})
```

View File

@@ -0,0 +1,14 @@
---
title: "Sample TIL (Today I Learned)"
date: "2024-03-20"
lang: en
categories:
- tools
- quick-tip
---
A short note about something interesting I learned today.
TIL articles are typically shorter than full blog posts and contain quick tips or useful discoveries.
Replace this file with your actual TILs from Hugo.

View File

@@ -0,0 +1,14 @@
---
title: "Зразок TIL (Today I Learned)"
date: "2024-03-15"
lang: uk
categories:
- tools
- quick-tip
---
Короткий запис про щось цікаве, що я дізнався сьогодні.
TIL статті зазвичай коротші за повноцінні пости і містять швидкі поради або корисні знахідки.
Замініть цей файл своїми реальними TIL з Hugo.

78
vega-lite.lua Normal file
View File

@@ -0,0 +1,78 @@
-- vega-lite.lua
-- A Quarto filter to render vega-lite code blocks using vega-embed
local vegaEmbedIncluded = false
function ensureVegaEmbed()
if not vegaEmbedIncluded then
vegaEmbedIncluded = true
quarto.doc.include_text("in-header", [[
<script src="https://cdn.jsdelivr.net/npm/vega@5"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-lite@5"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-embed@6"></script>
]])
end
end
local counter = 0
function CodeBlock(block)
if block.classes:includes('vega-lite') then
ensureVegaEmbed()
counter = counter + 1
local divId = 'vega-viz-' .. counter
-- Get the spec as a string
local spec = block.text
-- Parse options from attributes
local echo = block.attributes['echo']
local codeFold = block.attributes['code-fold']
-- Default to showing code collapsed
if echo == nil then echo = "true" end
if codeFold == nil then codeFold = "true" end
local result = {}
-- Handle code display
if echo ~= "false" then
local codeBlock = pandoc.CodeBlock(spec, {class = "json"})
if codeFold == "true" or codeFold == "show" then
-- Wrap in collapsible details/summary
local openAttr = ""
if codeFold == "show" then
openAttr = " open"
end
local foldedCode = pandoc.RawBlock('html', string.format([[
<details%s>
<summary>Click to expand code</summary>
]], openAttr))
table.insert(result, foldedCode)
table.insert(result, codeBlock)
table.insert(result, pandoc.RawBlock('html', '</details>'))
else
-- Show code normally
table.insert(result, codeBlock)
end
end
-- Create the HTML output for the visualization
local html = string.format([[
<div class="vega-viz-container">
<div id="%s"></div>
</div>
<script type="text/javascript">
vegaEmbed('#%s', %s, {"actions": true});
</script>
]], divId, divId, spec)
table.insert(result, pandoc.RawBlock('html', html))
return result
end
end