🍙

古米度診断ツールで年齢を楽しく表現 - ClaudeCode×JavaScript実装解説

に公開

はじめに

誕生日を入力するだけで、あなたの年齢を「古米」で表現するユニークな診断ツールを開発しました。例えば25歳なら「古古古古古古古古古古古古古古古古古古古古古古古古古米」といった具合に、年齢分だけ「古」を並べてお米の名前を生成します。

この記事で分かること

  • JavaScript による正確な年齢計算の実装方法
  • WordPressとの連携による使用回数カウンター機能
  • レスポンシブ対応の日付選択UI の作り方
  • シンプルで効果的なシェア機能の実装

この記事の対象者

  • JavaScript 初心者から中級者の方
  • WordPressでインタラクティブなコンテンツを作りたい方
  • ユニークなWebアプリのアイデアを探している開発者

動作環境・使用技術

  • HTML5, CSS3, Vanilla JavaScript
  • WordPress + PHP
  • レスポンシブデザイン対応(iPhone SE~デスクトップ)

自己紹介

ホネグミ代表、応用情報技術者の資格を持つエンジニア×マーケターです。これまでIT系の会社役員を4年、独立して4年目になります。クライアントワークでは「こうしたい」を技術で形にすることを専門としていますが、最近は思想駆動型サービス開発の第一人者として、AIを活用した様々なサービス開発を続けています。

https://zenn.dev/5naokichi/articles/8f9446a136a874

サービス概要

古米度診断のスクリーンショット

https://hone-gumi.com/komai/

「古米度診断」は、年齢に応じて「古」の文字数を変化させるシンプルなアイデアから生まれました:

  • 0歳: 新米
  • 1歳: 古米
  • 2歳: 古古米
  • 25歳: 古古古古古古古古古古古古古古古古古古古古古古古古古米

実装した機能:

  • 年/月/日の3段階日付選択
  • リアルタイム年齢計算
  • 年齢に応じた「古米」名生成
  • 累計診断回数カウンター
  • SNSシェア機能(X、LINE)
  • レスポンシブ対応

技術スタック

フロントエンド:

  • HTML5(セマンティックなマークアップ)
  • CSS3(フレックスボックス、グラデーション、アニメーション)
  • Vanilla JavaScript(軽量実装)

バックエンド:

  • WordPress(CMS基盤)
  • PHP(AJAX処理)
  • WordPress AJAX API(利用回数管理)

実装手順

1. 基本HTML構造

古米度診断の基本構造です:

rice-checker.html
<div id="rice-checker-container">
    <h1>🌾 あなたの【古米度】診断</h1>
    <p>誕生日を入力すると、現在の正確な年齢分だけ「古」が並んだお米の名前を教えます!</p>
    
    <!-- 累計使用回数表示 -->
    <div class="usage-stats">
        <p class="total-usage">累計診断回数: <span id="total-count">読み込み中...</span></p>
    </div>
    
    <!-- 日付選択UI -->
    <div class="input-section">
        <label>誕生日を選択してください:</label>
        <div class="date-inputs">
            <select id="riceChecker-year"><option value=""></option></select>
            <select id="riceChecker-month"><option value=""></option></select>
            <select id="riceChecker-day"><option value=""></option></select>
        </div>
        <button id="riceChecker-checkButton">古米度を診断する</button>
    </div>
    
    <div id="riceChecker-result" class="result-section">
        <!-- 結果表示エリア -->
    </div>
</div>

2. 年齢計算ロジック

正確な年齢計算の実装が最も重要な部分です:

age-calculation.js
function calculateAge(birthDate, currentDate) {
    let age = currentDate.getFullYear() - birthDate.getFullYear();
    const monthDiff = currentDate.getMonth() - birthDate.getMonth();
    
    // 誕生日前の場合は年齢を1つ減らす
    if (monthDiff < 0 || (monthDiff === 0 && currentDate.getDate() < birthDate.getDate())) {
        age--;
    }
    
    return age;
}

3. 古米名生成機能

年齢に応じた古米名を生成する関数:

rice-name-generator.js
function generateRiceName(age) {
    let riceName = '';
    let className = 'fresh';
    
    if (age === 0) {
        riceName = '新米';
        className = 'fresh';
    } else if (age === 1) {
        riceName = '古米';
        className = 'old';
    } else if (age === 2) {
        riceName = '古古米';
        className = 'old';
    } else {
        // 3歳以上は年齢分「古」を繰り返し
        riceName = '古'.repeat(age) + '米';
        className = 'very-old';
    }
    
    return { riceName, className };
}

String.repeat() メソッドを使用することで、シンプルに文字の繰り返しを実装できます。

4. 日付選択UIの動的生成

ユーザビリティを重視した日付選択の実装:

date-selector.js
// 年のオプション生成(現在年から100年前まで)
const currentYear = new Date().getFullYear();
for (let year = currentYear; year >= currentYear - 100; year--) {
    const option = document.createElement('option');
    option.value = year;
    option.textContent = year;
    yearSelect.appendChild(option);
}

// 月に応じて日数を動的に更新
function updateDayOptions() {
    const year = parseInt(yearSelect.value);
    const month = parseInt(monthSelect.value);
    
    daySelect.innerHTML = '<option value="">日</option>';
    
    if (year && month) {
        // その月の日数を取得(うるう年対応)
        const daysInMonth = new Date(year, month, 0).getDate();
        for (let day = 1; day <= daysInMonth; day++) {
            const option = document.createElement('option');
            option.value = day;
            option.textContent = day;
            daySelect.appendChild(option);
        }
    }
}

yearSelect.addEventListener('change', updateDayOptions);
monthSelect.addEventListener('change', updateDayOptions);
うるう年の処理について

new Date(year, month, 0) を使用することで、指定した年月の前月の最終日(=当月の日数)を取得できます。この方法により、うるう年も自動的に考慮されます。

5. レスポンシブ対応

iPhone SE(375px)まで対応したレスポンシブデザイン:

responsive-design.css
/* iPhone SE (375px以下) 対応 */
@media (max-width: 380px) {
    .date-inputs {
        justify-content: center;
        gap: 5px;
        flex-wrap: nowrap;
    }
    
    select {
        width: 65px;
        font-size: 14px;
        padding: 8px 4px;
        min-width: 60px;
    }
    
    .date-label {
        font-size: 13px;
        margin: 0 2px;
    }
    
    button {
        width: 100%;
        font-size: 16px;
        padding: 12px 20px;
    }
}

/* 一般的なスマホサイズ (381px-600px) */
@media (max-width: 600px) and (min-width: 381px) {
    select {
        width: 80px;
        font-size: 15px;
        padding: 10px 8px;
    }
    
    button {
        width: 100%;
        font-size: 17px;
    }
}

6. WordPressとの連携(カウンター機能)

functions.php
// 古米度診断用カウンター機能
function rice_checker_counter_increment() {
    DiagnosisCounterSystem::increment_counter('rice_checker');
}

function rice_checker_counter_get() {
    DiagnosisCounterSystem::get_counter('rice_checker');
}

// Ajax アクション登録
add_action('wp_ajax_rice_counter', 'rice_checker_counter_increment');
add_action('wp_ajax_nopriv_rice_counter', 'rice_checker_counter_increment');
add_action('wp_ajax_rice_get_count', 'rice_checker_counter_get');
add_action('wp_ajax_nopriv_rice_get_count', 'rice_checker_counter_get');
counter-integration.js
// カウンター更新機能
function updateUsageCounter() {
    fetch('/wp-admin/admin-ajax.php', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: 'action=rice_counter'
    })
    .then(response => response.text())
    .then(count => {
        document.getElementById('total-count').textContent = count;
        
        // カウント更新時のアニメーション効果
        const countElement = document.getElementById('total-count');
        countElement.style.transform = 'scale(1.2)';
        countElement.style.transition = 'transform 0.3s ease';
        setTimeout(() => {
            countElement.style.transform = 'scale(1)';
        }, 300);
    })
    .catch(error => {
        console.error('カウンター更新エラー:', error);
    });
}

工夫したポイント・苦労した点

1. 日付選択UIの使いやすさ

年・月・日を別々のセレクトボックスに分けることで、スマートフォンでも操作しやすい設計にしました。特に重要だったのは:

  • 月が選択されたら、その月の日数に応じて日の選択肢を動的に更新
  • うるう年の正確な対応
  • 未来の日付を選択できないバリデーション

2. 極小画面への対応

iPhone SE(375px)という小さな画面でも快適に使えるよう、細かな調整を行いました:

  • セレクトボックスの幅とフォントサイズを画面サイズに応じて調整
  • ボタンはスマホでは全幅表示で押しやすく
  • 文字間隔とパディングを最適化

3. エンターテイメント性の追求

技術的な実装だけでなく、楽しんでもらえる要素も重視しました:

4. パフォーマンス最適化

  • Vanilla JavaScript を使用してライブラリの読み込み時間を削減
  • CSS アニメーションは transform を使用してハードウェア支援を活用
  • DOM操作を最小限に抑制

まとめ

古米度診断は、非常にシンプルなアイデアから始まったプロジェクトでしたが、技術的には学びの多い開発となりました。

主な学び:

  1. UXの重要性: どれだけシンプルなアプリでも、使いやすさが利用者数に直結する
  2. レスポンシブ対応の必要性: スマートフォンユーザーを意識した設計は必須
  3. エンターテイメント性: 技術だけでなく、「楽しさ」が拡散の鍵
  4. バリデーションの大切さ: 正確な年齢計算など、基本的な処理の精度が信頼性を左右

今回の開発を通じて、Claude Code との協業による効率的な開発プロセスも体験できました。AIを活用することで、アイデアから実装まで大幅に時間短縮できるのは大きなメリットです。

サービスはこちら

https://hone-gumi.com/komai/

免責事項

本ツールはエンターテイメント・パロディ目的で作成された診断ツールです。診断結果は架空のものであり、実際のお米の品質や保存状態とは一切関係ありません。本ツールの使用により生じた損害について、制作者は一切の責任を負いません。

Discussion