
「アイデアは浮かぶけど、すぐ忘れちゃうんだよな…」というあなたに朗報です!今回は、AI「Windsurf」にお願いして、サクッと使えるメモアプリを作ってもらいました。
HTML・CSS・JavaScriptの3点セットで、初心者でも簡単に動かせる構成になっています。
この記事では、完成したアプリの紹介から、実際に使ったプロンプト、そしてコードの中身まで、まるっとご紹介します!
シンプルで使いやすいメモアプリ
今回のメモアプリは、以下の3ファイルで構成されています。
index.html
:アプリの構造(見た目の枠組み)styles.css
:デザイン(見た目の装飾)script.js
:ロジック(動きや記憶の仕組み)
主な機能は以下のとおりです。
- メモの入力・保存
- 保存されたメモの一覧表示
- メモの編集・削除
- ブラウザのローカルストレージに保存(リロードしても残る!)
初心者でも迷わず使える、直感的なUIになっていて、「思いついたら即メモ」な使い方ができます!
使用プロンプトの紹介
Windsurfには、以下のようなプロンプトを送りました👇
Create a simple Memo App using HTML, CSS, and JavaScript.
Requirements:
- Users can type a memo and save it.
- Saved memos are displayed in a list.
- Each memo can be deleted.
- Data should be saved in localStorage, so it remains after reloading the page.
- Simple and clean UI using CSS.
Separate files:
- index.html for structure
- styles.css for design
- script.js for logic
Use modern JavaScript and semantic HTML.
- Each memo can be edited inline and saved again.
たったこれだけで、構造からデザイン、挙動まで整った3ファイルが生成されるって…正直ちょっと感動です。
ファイルごとのポイント解説
index.html:構造はシンプルに
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Memo App</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="container">
<h1>Memo App</h1>
<div class="memo-form">
<textarea id="memo-input" placeholder="Type your memo here..."></textarea>
<button id="save-button">Save Memo</button>
</div>
<div class="memos-container">
<h2>Your Memos</h2>
<div id="memos-list"></div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
テキストエリアとボタンだけのシンプルな構造。これだけでも立派なアプリになります。
<textarea id="memo-input" placeholder="Type your memo here..."></textarea>
<button id="save-button">Save Memo</button>
styles.css:見た目はモダンで親しみやすく
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Arial', sans-serif;
}
body {
background-color: #f5f5f5;
color: #333;
line-height: 1.6;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
h1 {
text-align: center;
margin-bottom: 20px;
color: #2c3e50;
}
h2 {
margin: 20px 0;
color: #2c3e50;
}
.memo-form {
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin-bottom: 20px;
}
#memo-input {
width: 100%;
height: 100px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
resize: vertical;
margin-bottom: 10px;
font-size: 16px;
}
#save-button {
background-color: #3498db;
color: white;
border: none;
padding: 10px 15px;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
transition: background-color 0.3s;
}
#save-button:hover {
background-color: #2980b9;
}
.memos-container {
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.memo-item {
border-bottom: 1px solid #eee;
padding: 15px 0;
display: flex;
justify-content: space-between;
align-items: flex-start;
}
.memo-content {
flex-grow: 1;
margin-right: 15px;
}
.memo-text {
font-size: 16px;
margin-bottom: 5px;
word-break: break-word;
}
.memo-actions {
display: flex;
gap: 10px;
}
.edit-button, .delete-button, .save-edit-button, .cancel-edit-button {
background-color: transparent;
border: none;
cursor: pointer;
padding: 5px;
border-radius: 4px;
font-size: 14px;
transition: background-color 0.3s;
}
.edit-button {
color: #2ecc71;
}
.delete-button {
color: #e74c3c;
}
.save-edit-button {
color: #3498db;
}
.cancel-edit-button {
color: #95a5a6;
}
.edit-button:hover, .delete-button:hover, .save-edit-button:hover, .cancel-edit-button:hover {
background-color: #f1f1f1;
}
.editing textarea {
width: 100%;
min-height: 80px;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
margin-bottom: 10px;
font-size: 16px;
resize: vertical;
}
.timestamp {
font-size: 12px;
color: #95a5a6;
margin-top: 5px;
}
@media (max-width: 600px) {
.container {
padding: 10px;
}
.memo-form, .memos-container {
padding: 15px;
}
}
全体的に余白やカラーの使い方がやさしく、長時間使っても疲れにくいデザインです。レスポンシブ対応もばっちりなので、スマホでも使いやすいです。
#memo-input {
width: 100%;
height: 100px;
padding: 10px;
border-radius: 4px;
}
script.js:ロジックがしっかり整理されていて読みやすい

document.addEventListener('DOMContentLoaded', () => {
// DOM Elements
const memoInput = document.getElementById('memo-input');
const saveButton = document.getElementById('save-button');
const memosList = document.getElementById('memos-list');
// Global variables
let memos = [];
let editingId = null;
// Load memos from localStorage
function loadMemos() {
const storedMemos = localStorage.getItem('memos');
if (storedMemos) {
memos = JSON.parse(storedMemos);
renderMemos();
}
}
// Save memos to localStorage
function saveMemos() {
localStorage.setItem('memos', JSON.stringify(memos));
}
// Render memos to the DOM
function renderMemos() {
memosList.innerHTML = '';
if (memos.length === 0) {
memosList.innerHTML = '<p class="no-memos">No memos yet. Create one!</p>';
return;
}
memos.forEach(memo => {
const memoItem = document.createElement('div');
memoItem.classList.add('memo-item');
memoItem.dataset.id = memo.id;
if (editingId === memo.id) {
// Editing mode
memoItem.classList.add('editing');
memoItem.innerHTML = `
<div class="memo-content">
<textarea class="edit-memo-input">${memo.text}</textarea>
<div class="memo-actions">
<button class="save-edit-button">Save</button>
<button class="cancel-edit-button">Cancel</button>
</div>
</div>
`;
const saveEditButton = memoItem.querySelector('.save-edit-button');
const cancelEditButton = memoItem.querySelector('.cancel-edit-button');
const editInput = memoItem.querySelector('.edit-memo-input');
saveEditButton.addEventListener('click', () => {
saveEdit(memo.id, editInput.value);
});
cancelEditButton.addEventListener('click', () => {
cancelEdit();
});
} else {
// Display mode
const date = new Date(memo.timestamp);
const formattedDate = date.toLocaleString();
memoItem.innerHTML = `
<div class="memo-content">
<div class="memo-text">${memo.text}</div>
<div class="timestamp">${formattedDate}</div>
</div>
<div class="memo-actions">
<button class="edit-button">Edit</button>
<button class="delete-button">Delete</button>
</div>
`;
const editButton = memoItem.querySelector('.edit-button');
const deleteButton = memoItem.querySelector('.delete-button');
editButton.addEventListener('click', () => {
startEdit(memo.id);
});
deleteButton.addEventListener('click', () => {
deleteMemo(memo.id);
});
}
memosList.appendChild(memoItem);
});
}
// Add new memo
function addMemo(text) {
if (text.trim() === '') return;
const newMemo = {
id: Date.now().toString(),
text: text,
timestamp: new Date().toISOString()
};
memos.unshift(newMemo); // Add to the beginning of the array
saveMemos();
renderMemos();
memoInput.value = '';
}
// Delete memo
function deleteMemo(id) {
memos = memos.filter(memo => memo.id !== id);
saveMemos();
renderMemos();
}
// Start editing memo
function startEdit(id) {
editingId = id;
renderMemos();
}
// Save edited memo
function saveEdit(id, newText) {
if (newText.trim() === '') return;
const memoIndex = memos.findIndex(memo => memo.id === id);
if (memoIndex !== -1) {
memos[memoIndex].text = newText;
memos[memoIndex].timestamp = new Date().toISOString(); // Update timestamp
editingId = null;
saveMemos();
renderMemos();
}
}
// Cancel editing
function cancelEdit() {
editingId = null;
renderMemos();
}
// Event Listeners
saveButton.addEventListener('click', () => {
addMemo(memoInput.value);
});
memoInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
addMemo(memoInput.value);
}
});
// Initialize
loadMemos();
});
ここでメモ情報をローカルストレージに保存しています。データはブラウザに記録されるため、ページを閉じても消えません。
localStorage.setItem('memos', JSON.stringify(memos));
さらに、編集モード・保存・キャンセルの処理も丁寧に分けられていて、拡張しやすい構造になっています👇
editingId
という状態管理用の変数をうまく使って、「いまどのメモを編集してるか?」を制御しているのが地味に賢い設計です。
function saveEdit(id, newText) { ... }
function cancelEdit() { ... }
AIと一緒なら、アプリ開発もこわくない!
今回のメモアプリ、機能はシンプルですが、「必要なことがしっかりできる」って感じで非常に実用的でした。
WindsurfのようなAIは、ちょっとしたアイデアをすぐ形にしてくれる「デジタル相棒」的存在。特に初心者にとっては、面倒な初期設定を肩代わりしてくれるありがた〜い存在なんです。
次は、カテゴリ分けできるメモアプリとか、リマインダー付きメモとかも挑戦してみたいですね!
「プログラミングに自信がない…」という方でも、まずはAIと一緒に「小さく作る」経験をしてみると、すごく世界が広がりますよ!