From Bear Blog to Jekyll. Migrating 100 Posts and Redesigning Everything
Today, I migrated all posts from Bear Blog to my Jekyll blog (theorakim.github.io) and significantly restructured the site. Work log with Claude Code.
1. Migration of Technology Philosophy Posts (16 articles)
I started by extracting technology philosophy-related posts from byminseokcom/post_export.csv. The CSV is the entire 889KB Bear Blog export file. Since the all tags column contained tags as a JSON array, I filtered posts with the tags technology philosophy, STS, Art-n-Tech, and human-ai.
Seventeen matched, but one had publish: False, so I converted sixteen to Jekyll posts. I automated front matter generation and body text extraction using a Python script.
Angle Bracket Issue
The Algorithm Rebellion Club #1 post was broken. Text like <Meeting Leader Self-Introduction>, <Presentation>, <Discussion> was interpreted as HTML tags and disappeared entirely. Solution:
- Section separators
<Presentation>,<Discussion>→**Presentation**,**Discussion**(bold formatting) - Work title
<Preparing to Meet the Future>→⟨Preparing to Meet the Future⟩(Unicode angle brackets)
Subsequently, implemented a script to automatically convert all posts using the same pattern.
2. Uploaded All Remaining Posts (81 pieces)
Uploaded the remaining 81 pieces at once, excluding those with publish:false and technical philosophy posts. Additional work here:
- Tag normalization: Unified
essay→에세이 - 11 untagged posts: Manually assigned appropriate tags based on content (e.g., essay, reading, observation)
- Excluded year tags: Hidden year tags like
year-2024in the UI
Tag Filter UI
Added a client-side tag filter to _layouts/archive.html. Structure: Liquid collects tag lists at build time, JS filters posts on click. Year group headings automatically hide empty years.
3. Page Separation
Separated writings, techphil, and devlog pages to avoid overlapping posts:
- writings (
exclude_tags): Excludes posts tagged with tech philosophy or development logs - techphil (
filter_tags): Only includes tech philosophy, STS, Art-n-Tech, human-ai - devlog (
filter_tags): Development logs only
Implemented two filter modes in the archive.html layout: filter_tags (include) and exclude_tags (exclude). This allows each page to function simply by declaring its settings in the front matter.
Resolving devlog confusion
There was an issue where tech philosophy posts were appearing mixed in with devlog entries. Two causes:
Claude.AIwas included in devlog'sfilter_tags, matching tech philosophy posts → Removed- Three existing devlog posts had no
개발기록tag at all → Added
4. From Modal to Page Navigation
Originally, clicking clicking (1) Write, (2) Tech Philosophy, or (3) Code Log would open a modal overlay displaying the post list. Changed this to directly navigate to each page (/writings/, /techphil/, /devlog/).
home.html: Replaceddata-modallink → Actual URL link- Utilized the modal
<section>- Deletedmodal.js, removed script tags fromdefault.html` - Cleaned up related CSS in
_layout.scss:.modal,.modal-close,.list-item, etc.
5. Design Adjustments
- Left alignment: Changed
margin: 0 auto→margin: 0. Left-aligned instead of centered. - Footer:
contact : summerinloving@gmail.com+© Kim Minseok · Font: Goun Dotum.(includes link) - Intro text: Bold applied then removed — ultimately kept original style
6. Local Build Environment
Spent considerable time troubleshooting Jekyll local build failures.
- System Ruby 2.6: Too outdated, bundler version mismatch
- Homebrew Ruby 4.0.1:
github-pagesgem's Liquid 4.0.3 callstainted?method, removed in Ruby 3.2+ - Tried Jekyll 4.x:
google-protobufnative extension build failed - Final Solution:
brew install ruby@3.3→github-pagesgem worked fine with Ruby 3.3.10 + Bundler 2.7.1
7. Book Spread Layout
Today's highlight. Implemented a "book spread" two-column layout on the archive page.
Structure
- Left Panel (420px): Page title + Tag filter + Post list. Fixed with
position: stickyduring scroll, internally overflow scroll. - Divider:
border-right: 1px solid - Right Panel (flex: 1, max-width 640px): Body of the selected post
How It Works
On desktop (960px+), clicking a post list link fetches the post's HTML via fetch(). It parses the HTML with DOMParser, extracts only the .post article element, and inserts it into the right panel. A cache object prevents re-fetching the same article, and history.replaceState updates the URL.
On mobile (under 960px), the default link behavior takes effect without JS intervention, simply navigating the page.
CSS Encoding Struggles
Adding Korean comments (// 책 날개) and Unicode characters (content: "●") to SCSS files caused Jekyll's sass-converter 1.5.2 to throw an Invalid US-ASCII character error. Fixed by using English comments and CSS escapes (\25CF) for special characters.
Modified Files
_layouts/archive.html— spread wrapper + reader JS_sass/_post.scss— spread layout CSS_sass/_layout.scss—body:has(.spread)max-width extension
8. Portfolio Page
Filled the previously empty /portfolio/ page. Organized into categories: self-made projects, work achievements, community activities, and writing.
Categories and Items
- Projects: Creative Morning Calendar (Discourse integration), Pangyo Weather Bot(Korea Meteorological Administration API + KakaoTalk)
- Work: Retrospective Archive (tfi-retro-library), Tech for Impact GitBook
- Community: Netfliq Yeonga Meetup
- Writing: Publy Articles
Each item consists of a title + 2-3 line description + technical tags. The entire block is a single <a> block, so clicking anywhere on the card navigates to it. I pre-embedded the data-id attribute to allow adding a spread layout later.
I also tried a 3-column grid card layout, but it clashed with the blog's minimal tone, so I finalized the vertical list.
Modified Files
portfolio.html— Category-specific HTML structure_sass/_portfolio.scss— New file, BEM structure stylesassets/css/main.scss— Added@import "portfolio"
9. Refining the Homepage
Emoji Replacement
Replaced numbers in the intro text (1) Write (2) Reflect on technology (3) Record code with emojis: ✍️ Write 💬 Reflect on technology 💻 Record code. Applied to both home.html and nav.html.
Header Letter Spacing
Added letter-spacing: 0.05em to the "Kim Minseok" header displayed on all pages. Tried adding bold but removed it — bold was too thick with the Goun Dotum font.
Expanding the Home Page Width
In default.html, <body id=""> functionality to add body_id: home to the home.html layout. In CSS, body#home { max-width: 740px } was applied to widen only the home section. Expanded from the default 640px by 100px. This was to naturally adjust the line break position of the intro text, fine-tuned by reducing the width from 800px → 760px → 750px → 740px.
However, placing body_id in the layout front matter caused issues where page.body_id wouldn't be captured. Since Jekyll requires accessing layout variables via layout.body_id, I modified default.html to page.body_id | default: layout.body_id.
Modified Files
_layouts/home.html— Emoji +body_id: home_layouts/default.html— Addedlayout.body_idfallback_includes/nav.html— Emoji_sass/_layout.scss— Header letter spacing +body#homewidth
Final Results
- 100 posts migrated (16 tech philosophy + 81 general + 3 existing devlog)
- 3 sections cleanly separated (writings / techphil / devlog)
- Tag filter UI functional
- Book-style layout — Side-by-side reading of list and body on PC
- Local build functioning normally (Ruby 3.3)
- Portfolio page — 4 categories: Projects·Work·Community·Articles
- Home page — Emoji, spacing, width adjustments
10. Terracotta Design Implementation
Changed the blog's background color from pure white (#ffffff) to Pampas (#F4F3EE) from the Terracotta palette. This matches the tone of my VSCode custom theme (Terracotta Light). Dark mode retains the original #1a1a1a.
Also tweaked the footer. Removed the separator (border-top), reduced margins, and consolidated content into a single line: © minseok | summerinloving@gmail.com. Reduced body padding and header margins so the footer is visible on a single screen without scrolling.
- Body padding:
--space-xl(6rem) →--space-lg(4rem) - Bottom header margin:
--space-lg(4rem) →--space-md(2rem) - Top footer margin:
--space-xl(6rem) →--space-md(2rem)
11. Homepage Spread Layout
Applied the book spread pattern used on the archive page to the homepage. Structure features an introduction on the left and content unfolding on the right.
Behavior
- Click "My Work" → Fetches and displays
/portfolio/content in the right panel - Click "Newsletter" → Displays
/newsletter/content - Mobile: Page navigation remains unchanged
Default State: 7 Recent Posts
To avoid an empty right panel, it displays 7 recent posts with date+tag when nothing is selected. Generated at build time using Liquid.
Active Link Indication
Initially used ● circles like the archive, but since the home page only has 2 links, shading worked better. Tried a black background but it stood out too much, so changed to terracotta Crail (#C15F3C) background + Pampas (#F4F3EE) text.
Left Panel Width Adjustment
I iterated through widths: 420px → 760px → 600px → 580px. 760px was too wide, making the right side cramped, while 580px balanced the introduction text wrapping and the right content width. Since the same value applies to the archive page, the entire spread layout changed consistently.
Modified Files
_layouts/home.html— spread structure + fetch JS_sass/_layout.scss—body#home:has(.spread)max-width, active link styles, recent posts styles_sass/_post.scss—.spread__leftwidth 580px,.spread__right > :first-childmargin removal
12. Newsletter Page
Created a new /newsletter/ page. Initially added a Substack embed iframe, but it clashed with the blog design. Removed the iframe and replaced it with an introduction text + terracotta-colored subscribe button.
newsletter.html— Intro text + Substack link button_sass/_layout.scss—.newsletter__btnstyle (Crail background, Crail Dark on hover)
13. Obsidian Blogging Workflow
Set up a system for comfortable writing and publishing.
Structure
The _claudecode-notes folder in the Obsidian vault is linked via a symbolic link to Jekyll's _posts/ directory. This means posts written in Obsidian become Jekyll posts directly.
옵시디언 vault/_claudecode-notes/
→ workspace/_claudecode-notes/ (심볼릭 링크)
→ theorakim.github.io/_posts/ (심볼릭 링크)
Template
I created a _templates/blog-post.md template in the Obsidian vault. When creating a new post, Cmd+P → Insert Template automatically fills in the front matter.
Publishing Workflow
- Write the post in Obsidian's
_claudecode-notes(filename:YYYY-MM-DD-slug.md) - In Claude Code, say "Publish this" → Check syntax + Verify build + Git commit & push
Drafts or notes can stay in other Obsidian folders; only move posts ready for publishing to _claudecode-notes. Git push is intentionally a manual step to prevent accidental publishing.
14. Home Recent Posts → Spread Link
Changed the "Recent Posts" list on the homepage: clicking now links to an archive spread page showing both the list and the full post body, instead of the standalone post page.
Each post's tags are checked via Liquid to store the appropriate archive page in the data-archive attribute. On desktop, clicking takes you to that page with the ?read= query parameter. archive.html detects this parameter on load and automatically fetches the post. On mobile, it still goes to the single post page as before.
- Tech Philosophy/STS/Art-n-Tech/human-ai →
/techphil/ - Development Log →
/devlog/ - Everything else →
/writings/
15. Rebranding: byminseok.com
Changed the header's "Kim Minseok" to "byminseok.com" and adjusted it to font-weight: 600 (semi-bold). This structure makes the domain name the site's identity.
Updated the site description to I write, reflect on technology, and document code. Removed the redundant `
` tag and deleted three `