<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: shumatsumonobu</title>
    <description>The latest articles on DEV Community by shumatsumonobu (@shumatsumonobu).</description>
    <link>https://dev.to/shumatsumonobu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3846278%2F87ecb0db-1aef-45b7-8434-43eb78db9b6f.jpeg</url>
      <title>DEV Community: shumatsumonobu</title>
      <link>https://dev.to/shumatsumonobu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shumatsumonobu"/>
    <language>en</language>
    <item>
      <title>I Built a Personality Check That Reads Your Image Swipes</title>
      <dc:creator>shumatsumonobu</dc:creator>
      <pubDate>Tue, 07 Apr 2026 01:24:53 +0000</pubDate>
      <link>https://dev.to/shumatsumonobu/i-built-a-personality-check-that-reads-your-image-swipes-36e8</link>
      <guid>https://dev.to/shumatsumonobu/i-built-a-personality-check-that-reads-your-image-swipes-36e8</guid>
      <description>&lt;h2&gt;
  
  
  The Idea
&lt;/h2&gt;

&lt;p&gt;What if you could check your personality just by swiping images?&lt;/p&gt;

&lt;p&gt;Not a 50-question quiz. Not a wall of text. Just images.&lt;/p&gt;

&lt;p&gt;Swipe right for "That's me!" — swipe left for "Not me." 10 images, 10 seconds.&lt;/p&gt;

&lt;p&gt;That's &lt;a href="https://swipeocean.surf/about?l=en" rel="noopener noreferrer"&gt;Swipe Ocean&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;The app is based on the &lt;strong&gt;Big Five (OCEAN)&lt;/strong&gt; personality model — the most scientifically validated framework in psychology.&lt;/p&gt;

&lt;p&gt;Each image maps to one of 5 traits (Openness, Conscientiousness, Extraversion, Agreeableness, Sensitivity). Your swipe pattern reveals your balance across these traits, and you get matched with one of 8 personality types.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvpz0atcvk5xm3ol86ovu.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvpz0atcvk5xm3ol86ovu.gif" alt=" " width="400" height="866"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The key insight: &lt;strong&gt;your results change with your mood.&lt;/strong&gt; Take it on Monday morning vs Friday night — you might get a completely different type. And that's the point. It's not a label. It's a snapshot of who you are &lt;em&gt;today&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js 16&lt;/strong&gt; (App Router) + React 19 + TypeScript&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tailwind CSS 3&lt;/strong&gt; — dark theme with gradient backgrounds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare Workers&lt;/strong&gt; via @opennextjs/cloudflare&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemini AI&lt;/strong&gt; — personalized type descriptions based on your scores&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sharp&lt;/strong&gt; — OGP image generation (static, not dynamic — Workers can't run resvg-wasm)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Scoring Algorithm
&lt;/h2&gt;

&lt;p&gt;Each swipe changes your trait scores:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Swipe&lt;/th&gt;
&lt;th&gt;Direction&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;"That's me!" →&lt;/td&gt;
&lt;td&gt;high trait&lt;/td&gt;
&lt;td&gt;+0.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"That's me!" →&lt;/td&gt;
&lt;td&gt;low trait&lt;/td&gt;
&lt;td&gt;-0.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;← "Not me"&lt;/td&gt;
&lt;td&gt;high trait&lt;/td&gt;
&lt;td&gt;-0.05&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;← "Not me"&lt;/td&gt;
&lt;td&gt;low trait&lt;/td&gt;
&lt;td&gt;+0.05&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;"That's me!" swipes have &lt;strong&gt;2x the impact&lt;/strong&gt; of "Not me" swipes. Your gut feelings shape your scores.&lt;/p&gt;

&lt;p&gt;The full scoring logic is transparent — check the &lt;a href="https://swipeocean.surf/about?l=en" rel="noopener noreferrer"&gt;/about page&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Personalization
&lt;/h2&gt;

&lt;p&gt;After scoring, Gemini AI generates a unique description for your type based on your exact scores. Same type, different scores = different text every time.&lt;/p&gt;

&lt;p&gt;The AI runs server-side via a Next.js API route. If it fails (rate limit, timeout), the app falls back to pre-written descriptions. Progressive enhancement — the core experience never breaks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo5265bljuqhvbds9gsje.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo5265bljuqhvbds9gsje.png" alt="Result page" width="780" height="3356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Cloudflare Workers Gotchas
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;next/og&lt;/code&gt; doesn't work&lt;/strong&gt; — &lt;code&gt;resvg-wasm&lt;/code&gt; requires a &lt;code&gt;?module&lt;/code&gt; import that Workers don't support. Solution: pre-generate all OGP images with Sharp scripts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Secrets aren't in &lt;code&gt;process.env&lt;/code&gt;&lt;/strong&gt; — &lt;code&gt;@opennextjs/cloudflare&lt;/code&gt; doesn't map wrangler secrets to &lt;code&gt;process.env&lt;/code&gt;. Use &lt;code&gt;getCloudflareContext().env&lt;/code&gt; instead.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;WSL required for deploy&lt;/strong&gt; — Windows can't build the Workers bundle due to native binary issues. Deploy script auto-switches to WSL.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;→ &lt;strong&gt;&lt;a href="https://swipeocean.surf/?l=en" rel="noopener noreferrer"&gt;swipeocean.surf&lt;/a&gt;&lt;/strong&gt; — 10 swipes, 10 seconds, find your color today&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What type did you get?&lt;/strong&gt; Drop a comment — curious to see if developers skew toward Explorer or Planner 🤔&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built by &lt;a href="https://x.com/shumatsumonobu" rel="noopener noreferrer"&gt;@shumatsumonobu&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>cloudflare</category>
      <category>webdev</category>
      <category>showdev</category>
    </item>
    <item>
      <title>I Built a Hosting Diagnostic Tool on Cloudflare Workers</title>
      <dc:creator>shumatsumonobu</dc:creator>
      <pubDate>Mon, 30 Mar 2026 10:12:19 +0000</pubDate>
      <link>https://dev.to/shumatsumonobu/i-built-a-hosting-diagnostic-tool-on-cloudflare-workers-ao4</link>
      <guid>https://dev.to/shumatsumonobu/i-built-a-hosting-diagnostic-tool-on-cloudflare-workers-ao4</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;hostme &lt;span class="nt"&gt;--why&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every time I finish building something, I hit the same wall: &lt;em&gt;where do I deploy this?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Vercel? Netlify? Cloudflare? Railway? The options keep growing, and each one has different pricing, limits, and trade-offs. I wanted a tool that could cut through the noise — something fast, transparent, and opinionated enough to actually help.&lt;/p&gt;

&lt;p&gt;So I built &lt;a href="https://hostme.dev/lp?l=en" rel="noopener noreferrer"&gt;hostme&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Does
&lt;/h2&gt;

&lt;p&gt;hostme is a terminal-style web tool that asks you 7 questions about your project and scores 10 hosting services to recommend your TOP 3.&lt;/p&gt;

&lt;p&gt;The whole flow takes about 30 seconds. No signup. No tracking.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkl6eoz4prb1s7rd5zgh2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkl6eoz4prb1s7rd5zgh2.gif" alt="hostme demo - 7 questions to find your ideal hosting" width="600" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js&lt;/strong&gt; (App Router) + TypeScript&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tailwind CSS v4&lt;/strong&gt; — runtime theme variables via CSS custom properties&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;framer-motion&lt;/strong&gt; — scroll animations on the landing page&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare Workers&lt;/strong&gt; via @opennextjs/cloudflare&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic OGP&lt;/strong&gt; — next/og ImageResponse&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Building the Terminal UI
&lt;/h2&gt;

&lt;p&gt;The entire UI is designed to look like a terminal. Here's the approach:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Typing animation&lt;/strong&gt;: A custom &lt;code&gt;TypingText&lt;/code&gt; component renders text character by character. Speed scales with text length — short strings type slower (more dramatic), long strings type faster (less tedious).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keyboard navigation&lt;/strong&gt;: Every page responds to keyboard input. Number keys select options, Enter confirms, Backspace goes back. The goal: you should be able to complete the entire diagnosis without touching the mouse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CSS theming&lt;/strong&gt;: All colors are CSS custom properties in &lt;code&gt;:root&lt;/code&gt;. The terminal green (&lt;code&gt;#00ff41&lt;/code&gt; on &lt;code&gt;#0a1a0a&lt;/code&gt;) gives that retro feel while maintaining WCAG contrast ratios.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9xo8dyfjgenp2oisdrbd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9xo8dyfjgenp2oisdrbd.png" alt="Terminal UI showing the first question" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Scoring Algorithm
&lt;/h2&gt;

&lt;p&gt;Each of the 7 questions maps to a score matrix: every answer awards 0–3 points to each of the 10 services.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scoreMatrix[question][answer][service] → 0 | 1 | 2 | 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;3&lt;/strong&gt; = ideal match&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2&lt;/strong&gt; = good fit&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1&lt;/strong&gt; = acceptable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;0&lt;/strong&gt; = not recommended&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Region bonuses are applied separately — e.g., Cloudflare Workers gets +2 in Asia due to its edge network.&lt;/p&gt;

&lt;p&gt;The scoring logic is fully transparent: users can view the complete matrix on the &lt;a href="https://hostme.dev/about?l=en" rel="noopener noreferrer"&gt;/about&lt;/a&gt; page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq16e3kzh74d3bdqlr9a5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq16e3kzh74d3bdqlr9a5.png" alt="Scoring matrix on the /about page" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dynamic OGP on Cloudflare Workers
&lt;/h2&gt;

&lt;p&gt;This was the trickiest part. I wanted each diagnosis result to generate a unique OGP image showing the user's #1 recommendation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;: &lt;code&gt;next/og&lt;/code&gt; (powered by Satori) works on Cloudflare Workers, but font loading via self-referencing fetch fails silently. Workers block loopback requests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The solution&lt;/strong&gt;: Load fonts from Google Fonts CDN with a try-catch fallback:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;fontData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;ArrayBuffer&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://fonts.gstatic.com/s/jetbrainsmono/...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;fontData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Fall back to default font&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ImageResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;OgpComponent&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;ogpOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fontData&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the font fetch fails, the image still renders with a default font. No 500 errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lightweight i18n
&lt;/h2&gt;

&lt;p&gt;Instead of heavy i18n libraries, all UI text lives in a simple &lt;code&gt;Record&amp;lt;Lang, TextMap&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uiText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Lang&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;UiText&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;ja&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;診断スタート&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;en&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Start Diagnosis&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Language is determined by URL parameter (&lt;code&gt;?l=ja&lt;/code&gt;), and &lt;code&gt;document.documentElement.lang&lt;/code&gt; is set dynamically. Total i18n code: ~200 lines across 2 files.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare Workers have quirks&lt;/strong&gt; — no loopback fetch, Edge Runtime not always better than Node.js runtime&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terminal UIs are surprisingly engaging&lt;/strong&gt; — keyboard navigation makes the tool feel fast and fun&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transparent scoring builds trust&lt;/strong&gt; — showing the algorithm removes the "black box" skepticism&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;→ &lt;strong&gt;&lt;a href="https://hostme.dev/lp?l=en" rel="noopener noreferrer"&gt;hostme.dev&lt;/a&gt;&lt;/strong&gt; — find your ideal hosting in 30 seconds&lt;/p&gt;

&lt;p&gt;The scoring logic is fully transparent — check the &lt;a href="https://hostme.dev/about?l=en" rel="noopener noreferrer"&gt;/about&lt;/a&gt; page to see exactly how it works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's your go-to hosting for side projects? I'm curious how you decide.&lt;/strong&gt; Drop a comment — I'd love to compare notes.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built by &lt;a href="https://x.com/shumatsumonobu" rel="noopener noreferrer"&gt;@shumatsumonobu&lt;/a&gt;. We ship on Sundays.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>cloudflare</category>
      <category>webdev</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
