Build a Bookmark Manager with Claude Code
Build a full web app by talking to Claude Code in natural language.
Introduction
30 Second Summary
You probably have bookmarks scattered across different browsers and devices, making it impossible to find what you saved last week. What if you could build your own bookmark organizer without writing a single line of code yourself?
In this project, you will build a personal bookmark manager web app entirely through natural language conversations with Claude Code, an AI coding assistant that runs in your terminal.
What You'll Build
A single-page bookmark manager application using HTML, CSS, and JavaScript that lets you save, organize, and search bookmarks with tags.
By the end of this project, you'll have:
- Claude Code installed and authenticated in your terminal, ready for future AI-assisted projects.
- A working bookmark manager app that persists data in your browser using localStorage.
- A CLAUDE.md file that teaches Claude Code your project preferences and coding standards.
- Secret Mission: Add a dark mode toggle and refactor all colors to use CSS custom properties.
Are there any prerequisites?
You need basic familiarity with using a terminal (commands like cd and ls). No coding experience is required, though some familiarity with HTML, CSS, or JavaScript is helpful.
You also need a Claude Pro ($20/month) or Max subscription. The free Claude.ai plan does not include Claude Code access.
Before We Start
Before We Start...
Install Claude Code and Set Up Your Project
To build your bookmark manager, you need a way to talk to an AI that can write code for you.
That's where Claude Code comes in. It's a terminal-based tool that lets you describe what you want in plain English, and it writes the code, edits files, and manages your project. In this step, you'll install Claude Code, set up your project folder, and get authenticated.
In this step, get ready to:
- Install Claude Code and verify it's working.
- Create a project folder with version control.
- Launch Claude Code and authenticate with your account.
Install Claude Code using the native installer
Claude Code is a command-line tool that runs inside your terminal. You install it with a single command.
- Open your terminal.
- Install Claude Code by running this command:
macOS
curl -fsSL https://claude.ai/install.sh | bash
What does this command do?
This downloads the Claude Code installer script from Anthropic's servers and runs it on your machine. The -fsSL flags tell curl to follow redirects, stay silent, and fail cleanly on errors.
Windows
Open PowerShell (not CMD) and run:
irm https://claude.ai/install.ps1 | iex
What does this command do?
This downloads and executes the Claude Code PowerShell installer script. The irm command fetches the script, and iex executes it.
If you see the error 'irm' is not recognized, you're in CMD instead of PowerShell. Your prompt shows PS C:\ when you're in PowerShell.
- Verify the installation by running:
claude --version
✔️ I see a version number
Claude Code is installed and ready to go. Move on to the next substep.
ⓧ Command not found
The installer may not have added Claude Code to your system PATH. Try these fixes:
- Close your terminal and open a new one, then run claude --version again.
- Run claude doctor to check for configuration issues.
- If you're still stuck, visit the Claude Code troubleshooting docs for platform-specific fixes.
Create a project folder and initialize git
Claude Code works best inside a project that has version control. Git lets you track every change Claude Code makes, so you can always see what happened and roll back if needed.
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir bookmark-manager
cd bookmark-manager
- Initialize a git repository by running:
git init
- Confirm the folder and git setup by running:
ls -a
You should see a .git folder in the output. This means git is tracking your project.
Don't see the .git folder?
- Make sure you ran git init inside the bookmark-manager folder, not from your Desktop.
- Run pwd to check your current location. It should end with bookmark-manager.
Help me set up my git repository for a new project.
Launch Claude Code and authenticate
Now that your project folder is ready, you can start Claude Code inside it. The first time you launch, Claude Code will ask you to log in through your browser.
- Launch Claude Code by running:
claude
What happens when you run this?
Claude Code opens an interactive session in your terminal. The first time, it will display a URL and ask you to open it in your browser to authenticate.
This login connects Claude Code to your Claude Pro or Max subscription, which is required for Claude Code to work.
- When prompted, press Enter to open the authentication URL in your browser.
- Sign in with your Claude Pro or Max account credentials in the browser window that opens.
- After signing in, return to your terminal.
You should see the Claude Code welcome screen in your terminal. This means Claude Code is connected and ready to accept your instructions.
Authentication not working?
- Make sure you're signing in with an account that has a Claude Pro or Max subscription. The free Claude.ai plan does not include Claude Code access.
- If the browser didn't open automatically, copy the URL shown in your terminal and paste it into your browser manually.
- If you're already logged in but it's not connecting, type /login inside the Claude Code session to re-authenticate.
Why isn't my Claude Code authentication working?
Your development environment is ready. Next up, you'll use Claude Code to build the entire bookmark manager app through conversation.
Build the App Foundation with Claude Code
You have Claude Code running in your terminal, ready to take instructions. Now comes the exciting part: building an entire web application by having a conversation.
Instead of writing code line by line, you will describe what you want in plain English. Claude Code reads your prompt, plans the file structure, and writes all the code for you. Your job is to review what it proposes and test the result.
In this step, get ready to:
- Scaffold the bookmark manager app through a natural language prompt.
- Test the working app in your browser and explore the code.
- Commit your code using Claude Code's git integration.
Ask Claude Code to scaffold your project
The fastest way to start a new project with Claude Code is to describe everything you want in a single, detailed prompt. The more specific you are, the better the result.
- In your Claude Code session, type the following prompt and press Enter:
create a single-page bookmark manager app with HTML, CSS, and JavaScript. It should have a form to add bookmarks with a title, URL, and tags field, and display saved bookmarks in a list. Use localStorage to persist data. Keep everything in separate files: index.html, style.css, and app.js
What happens next?
Claude Code reads your prompt and plans the entire project structure. It then proposes changes to your files, showing you a diff of what it wants to create. You will see each new file listed with its contents.
Review the proposed changes. Claude Code presents an approval interface where you can accept or reject the edits before they are written to disk.
- Review the proposed file changes that Claude Code displays.
- Accept the changes when prompted to let Claude Code write the files to your project folder.
- Confirm the files exist by running:
ls
You should see index.html, style.css, and app.js listed in the output.
Don't see the three files?
Make sure you accepted the proposed changes when Claude Code asked. If you rejected them accidentally, type the same prompt again and accept this time.
If Claude Code created a different file structure (like a single file or extra folders), ask it to restructure: move everything into separate index.html, style.css, and app.js files in the current directory.
Help me troubleshoot my Claude Code file generation
Open the app in your browser and explore the code
Claude Code just built you a working web application. Since this is a plain HTML project with no build tools, you can open it directly in your browser to see it in action.
- Open the index.html file in your web browser. You can do this by double-clicking the file in Finder (macOS) or File Explorer (Windows), or by dragging it into a browser window.
- You should see a clean page with a "Bookmark Manager" heading, an "Add Bookmark" form with fields for Title, URL, and Tags, and a "Your Bookmarks" section below it.
- Test the app by typing NextWork in the Title field, https://www.nextwork.org in the URL field, and learning, projects in the Tags field.
- Click Save Bookmark.
Your bookmark appears in the list below the form. The data is saved to localStorage, so it will persist even if you refresh the page.
- Switch back to your Claude Code session in the terminal and ask it to explain the code:
explain how the bookmark storage works in app.js
Why ask Claude Code to explain?
Even though Claude Code wrote the code for you, understanding what it built makes you a better collaborator. When you ask Claude Code to explain its work, it walks through the logic step by step.
In this case, Claude Code will describe the BookmarkStorage module pattern: how getAll() reads from localStorage, how add() saves new bookmarks, and how the data is serialized as JSON.
Commit your code with Claude Code's git integration
Your project folder already has a Git repository initialized from the previous step. Claude Code can stage and commit files for you through conversation, writing a meaningful commit message automatically.
- In your Claude Code session, type the following to commit your new files:
commit all files with a descriptive message
Claude Code stages all three files and creates a commit with a message that describes what was built. You do not need to write the message yourself.
- Verify the commit by asking Claude Code to show your project history:
show me the git log
You should see one commit with a descriptive message summarizing the bookmark manager app creation.
Why use Claude Code for git?
Manually writing git commit messages is a small friction that adds up over time. Claude Code reads the actual changes you made and generates a message that accurately describes them. This keeps your project history clean and informative without extra effort.
Commit didn't work?
If Claude Code reports an error about git configuration, it may need your name and email for the commit. Ask Claude Code: set up git config with the name 'Developer' and email 'dev@example.com' then commit.
If Claude Code says there is nothing to commit, the files may have already been committed. Run show me the git log to check.
Help me fix my git commit in Claude Code
✔️ Awesome, I've got everything!
Great. Double check you have one commit in your git log with all three files (index.html, style.css, app.js) included.
ⓧ I'd like to double check the full code
Since Claude Code generates the code for you, your output may differ slightly in formatting or comments. The important thing is that the functionality matches. Here is a reference version of each file for comparison.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bookmark Manager</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<h1>Bookmark Manager</h1>
</header>
<main>
<section class="add-bookmark">
<h2>Add Bookmark</h2>
<form id="bookmark-form">
<label for="title">Title</label>
<input type="text" id="title" name="title" placeholder="My Favorite Site" required>
<label for="url">URL</label>
<input type="url" id="url" name="url" placeholder="https://example.com" required>
<label for="tags">Tags (comma-separated)</label>
<input type="text" id="tags" name="tags" placeholder="dev, tools, reference">
<button type="submit">Save Bookmark</button>
</form>
</section>
<section class="bookmark-list">
<h2>Your Bookmarks</h2>
<input type="text" id="search" placeholder="Search by title or tag..." aria-label="Search bookmarks">
<ul id="bookmarks"></ul>
</section>
</main>
<script src="app.js"></script>
</body>
</html>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
line-height: 1.6;
padding: 2rem;
max-width: 800px;
margin: 0 auto;
background-color: #f5f5f5;
color: #333;
}
header {
margin-bottom: 2rem;
}
header h1 {
font-size: 1.8rem;
color: #2c3e50;
}
.add-bookmark {
background: #fff;
padding: 1.5rem;
border-radius: 8px;
margin-bottom: 2rem;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.add-bookmark h2 {
margin-bottom: 1rem;
font-size: 1.2rem;
}
form {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
label {
font-weight: 600;
font-size: 0.9rem;
}
input[type="text"],
input[type="url"] {
padding: 0.6rem;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
}
input[type="text"]:focus,
input[type="url"]:focus {
outline: none;
border-color: #3498db;
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);
}
button[type="submit"] {
padding: 0.7rem 1.5rem;
background-color: #3498db;
color: #fff;
border: none;
border-radius: 4px;
font-size: 1rem;
cursor: pointer;
margin-top: 0.5rem;
}
button[type="submit"]:hover {
background-color: #2980b9;
}
.bookmark-list {
background: #fff;
padding: 1.5rem;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.bookmark-list h2 {
margin-bottom: 1rem;
font-size: 1.2rem;
}
#search {
width: 100%;
padding: 0.6rem;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
margin-bottom: 1rem;
}
#search:focus {
outline: none;
border-color: #3498db;
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);
}
#bookmarks {
list-style: none;
}
.bookmark-item {
display: flex;
flex-direction: column;
gap: 0.25rem;
padding: 1rem;
border-bottom: 1px solid #eee;
}
.bookmark-item:last-child {
border-bottom: none;
}
.bookmark-title {
font-weight: 600;
color: #2c3e50;
}
.bookmark-url a {
color: #3498db;
text-decoration: none;
font-size: 0.9rem;
}
.bookmark-url a:hover {
text-decoration: underline;
}
.bookmark-tags {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
margin-top: 0.25rem;
}
.tag {
background-color: #ecf0f1;
color: #7f8c8d;
padding: 0.15rem 0.5rem;
border-radius: 3px;
font-size: 0.8rem;
}
.bookmark-delete {
background: none;
border: none;
color: #e74c3c;
cursor: pointer;
font-size: 0.85rem;
align-self: flex-end;
padding: 0.25rem;
}
.bookmark-delete:hover {
text-decoration: underline;
}
/* Storage module - handles all localStorage operations */
const BookmarkStorage = {
KEY: "bookmarks",
getAll() {
const data = localStorage.getItem(this.KEY);
return data ? JSON.parse(data) : [];
},
save(bookmarks) {
localStorage.setItem(this.KEY, JSON.stringify(bookmarks));
},
add(bookmark) {
const bookmarks = this.getAll();
bookmarks.push(bookmark);
this.save(bookmarks);
return bookmarks;
},
remove(id) {
const bookmarks = this.getAll().filter(b => b.id !== id);
this.save(bookmarks);
return bookmarks;
}
};
/* UI module - handles rendering and user interaction */
const BookmarkUI = {
form: document.getElementById("bookmark-form"),
list: document.getElementById("bookmarks"),
searchInput: document.getElementById("search"),
init() {
this.form.addEventListener("submit", (e) => this.handleSubmit(e));
this.searchInput.addEventListener("keyup", () => this.handleSearch());
this.renderBookmarks(BookmarkStorage.getAll());
},
handleSubmit(event) {
event.preventDefault();
const title = document.getElementById("title").value.trim();
const url = document.getElementById("url").value.trim();
const tagsRaw = document.getElementById("tags").value.trim();
/* Split tags by comma and trim each one individually */
const tags = tagsRaw
? tagsRaw.split(",").map(tag => tag.trim()).filter(tag => tag !== "")
: [];
const bookmark = {
id: Date.now().toString(),
title,
url,
tags,
createdAt: new Date().toISOString()
};
const bookmarks = BookmarkStorage.add(bookmark);
this.renderBookmarks(bookmarks);
this.form.reset();
},
handleSearch() {
const query = this.searchInput.value.toLowerCase().trim();
const bookmarks = BookmarkStorage.getAll();
if (!query) {
this.renderBookmarks(bookmarks);
return;
}
/* Filter by title or individual tags */
const filtered = bookmarks.filter(bookmark => {
const titleMatch = bookmark.title.toLowerCase().includes(query);
const tagMatch = bookmark.tags.some(tag =>
tag.toLowerCase().includes(query)
);
return titleMatch || tagMatch;
});
this.renderBookmarks(filtered);
},
renderBookmarks(bookmarks) {
if (bookmarks.length === 0) {
this.list.innerHTML = "<li class=\"bookmark-item\">No bookmarks yet. Add one above!</li>";
return;
}
this.list.innerHTML = bookmarks.map(bookmark => `
<li class="bookmark-item">
<span class="bookmark-title">${this.escapeHtml(bookmark.title)}</span>
<span class="bookmark-url">
<a href="${this.escapeHtml(bookmark.url)}" target="_blank" rel="noopener noreferrer">
${this.escapeHtml(bookmark.url)}
</a>
</span>
${bookmark.tags.length > 0 ? `
<span class="bookmark-tags">
${bookmark.tags.map(tag => `<span class="tag">${this.escapeHtml(tag)}</span>`).join("")}
</span>
` : ""}
<button class="bookmark-delete" data-id="${bookmark.id}">Delete</button>
</li>
`).join("");
/* Attach delete handlers */
this.list.querySelectorAll(".bookmark-delete").forEach(button => {
button.addEventListener("click", (e) => {
const id = e.target.dataset.id;
const updated = BookmarkStorage.remove(id);
this.renderBookmarks(updated);
});
});
},
escapeHtml(text) {
const div = document.createElement("div");
div.textContent = text;
return div.innerHTML;
}
};
/* Initialize the app */
BookmarkUI.init();
You now have a working bookmark manager built entirely through conversation. Next up, you will create a CLAUDE.md file to teach Claude Code your coding preferences, then add a search feature that filters bookmarks in real time.
Create CLAUDE.md and Add Search Functionality
Your bookmark manager app is working and committed to git. Now it's time to teach Claude Code your coding preferences so every future change follows your standards.
Claude Code reads a special file called CLAUDE.md at the start of every session. This file acts like a persistent briefing that tells Claude how you want your code written. You'll create one now, then use it to guide a new feature: a search bar that filters bookmarks instantly as you type.
In this step, get ready to:
- Create a CLAUDE.md file with your project's coding standards.
- Add real-time search functionality using natural language.
- Commit and review your changes with Claude Code's git integration.
Create a CLAUDE.md file with your project preferences
Every time Claude Code starts a session, it looks for a CLAUDE.md file in your project. Whatever instructions you put there get loaded automatically, so Claude follows your conventions without you repeating yourself.
- In your Claude Code session, type the following prompt and press Enter:
create a CLAUDE.md file for this project with these instructions: use semantic HTML, keep JavaScript functions small and well-named, use CSS flexbox for layout, add comments explaining complex logic, and always test changes by describing what to verify in the browser
What is CLAUDE.md?
CLAUDE.md is loaded into Claude Code's context at the start of every session. It's like handing a coworker a style guide before they start contributing to your project.
Your instructions tell Claude to use semantic HTML, keep functions small, use flexbox, and add comments. Claude will follow these conventions in every future change it makes.
- Claude Code will propose creating a new CLAUDE.md file. Review the contents and approve the change.
- Confirm the file was created by asking Claude Code:
show me the contents of CLAUDE.md
You should see sections covering coding standards, project structure, testing guidelines, and conventions.
What should I see in CLAUDE.md?
Your file should include headings for Coding Standards, Project Structure, Testing, and Conventions. The exact wording may vary slightly, but the key rules (semantic HTML, small functions, flexbox, comments, testing guidance) should all be present.
CLAUDE.md looks empty or wasn't created?
Try the prompt again. Make sure you're still inside your bookmark-manager folder in the Claude Code session. If Claude Code asks for confirmation to write the file, type y to approve.
Help me create a CLAUDE.md file for my project
Add a search bar that filters bookmarks in real time
Now that Claude Code knows your coding standards, let's put them to the test. You'll ask for a search feature and watch how Claude edits multiple files while following your CLAUDE.md conventions.
- Type this prompt in your Claude Code session:
add a search bar that filters bookmarks by title or tag as you type. Make it fast with no submit button needed, just filter on keyup
- Claude Code will propose changes across multiple files. Review what it suggests for each file, then approve the changes.
What does Claude Code change?
Claude Code edits three files to implement this feature:
- index.html gets a new search input field above the bookmarks list.
- style.css gets styling for the search input.
- app.js gets filter logic that runs on every keystroke, matching against both titles and individual tags.
Notice how Claude follows your CLAUDE.md conventions: the search function uses descriptive naming, the layout uses flexbox, and comments explain the filtering logic.
- Open index.html in your browser to test the search feature.
- Add two or three bookmarks with different tags (for example, one with tags dev, tools and another with news, tech).
- Type a tag name into the search bar. Your bookmark list should filter instantly as you type, with no page reload needed.
Search not filtering results?
Make sure you approved all of Claude Code's proposed changes. If the search input appears but doesn't filter, check that app.js was updated with the filter logic. You can ask Claude Code to verify:
explain how the search filtering works in app.js
Help me debug why my search bar isn't filtering bookmarks
Commit your changes and review the approach
You've added two important pieces to your project: persistent coding standards and a search feature. Let's commit everything and review Claude Code's work.
- Ask Claude Code to commit by typing:
commit these changes
- Claude Code will stage all changed files and create a commit with a descriptive message. Approve the commit when prompted.
- Now ask Claude Code to summarize what it did:
show me what files changed and summarize the approach you took
Why review Claude Code's summary?
Asking Claude Code to explain its approach helps you understand the code it wrote. This is especially useful when Claude edits multiple files at once. You can catch anything that doesn't match your expectations before moving on.
- Verify your git history now has two commits by typing:
show me the git log
You should see two commits: your initial scaffold from the previous step and the new commit for CLAUDE.md and search.
Only seeing one commit?
If the commit didn't go through, Claude Code may have been waiting for approval. Type commit these changes again and make sure to approve when prompted. Check that you're still in the bookmark-manager directory.
Help me commit my changes with Claude Code
✔️ Awesome, I've got everything!
Great. Confirm you have a CLAUDE.md file in your project and that search is working in the browser.
ⓧ I'd like to double check the full code
Here's what your CLAUDE.md file should contain:
# Bookmark Manager - Project Instructions
## Coding Standards
- Use semantic HTML elements (header, main, section, nav, article)
- Keep JavaScript functions small and well-named (max ~20 lines per function)
- Use CSS flexbox for layout
- Add comments explaining complex logic
## Project Structure
- index.html - Main page markup
- style.css - All styles
- app.js - Application logic using module pattern (BookmarkStorage and BookmarkUI)
## Testing
- After making changes, describe what to verify in the browser
- Test that bookmarks persist after page reload
- Test that search filters by both title and individual tags
## Conventions
- Use localStorage for data persistence
- Tags are stored as arrays, split by comma on input
- Each bookmark has: id, title, url, tags (array), createdAt
Your index.html should include a search input above the bookmarks list:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bookmark Manager</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<h1>Bookmark Manager</h1>
</header>
<main>
<section class="add-bookmark">
<h2>Add Bookmark</h2>
<form id="bookmark-form">
<label for="title">Title</label>
<input type="text" id="title" name="title" placeholder="My Favorite Site" required>
<label for="url">URL</label>
<input type="url" id="url" name="url" placeholder="https://example.com" required>
<label for="tags">Tags (comma-separated)</label>
<input type="text" id="tags" name="tags" placeholder="dev, tools, reference">
<button type="submit">Save Bookmark</button>
</form>
</section>
<section class="bookmark-list">
<h2>Your Bookmarks</h2>
<input type="text" id="search" placeholder="Search by title or tag..." aria-label="Search bookmarks">
<ul id="bookmarks"></ul>
</section>
</main>
<script src="app.js"></script>
</body>
</html>
Your app.js should include the handleSearch() method in BookmarkUI:
/* Storage module - handles all localStorage operations */
const BookmarkStorage = {
KEY: "bookmarks",
getAll() {
const data = localStorage.getItem(this.KEY);
return data ? JSON.parse(data) : [];
},
save(bookmarks) {
localStorage.setItem(this.KEY, JSON.stringify(bookmarks));
},
add(bookmark) {
const bookmarks = this.getAll();
bookmarks.push(bookmark);
this.save(bookmarks);
return bookmarks;
},
remove(id) {
const bookmarks = this.getAll().filter(b => b.id !== id);
this.save(bookmarks);
return bookmarks;
}
};
/* UI module - handles rendering and user interaction */
const BookmarkUI = {
form: document.getElementById("bookmark-form"),
list: document.getElementById("bookmarks"),
searchInput: document.getElementById("search"),
init() {
this.form.addEventListener("submit", (e) => this.handleSubmit(e));
this.searchInput.addEventListener("keyup", () => this.handleSearch());
this.renderBookmarks(BookmarkStorage.getAll());
},
handleSubmit(event) {
event.preventDefault();
const title = document.getElementById("title").value.trim();
const url = document.getElementById("url").value.trim();
const tagsRaw = document.getElementById("tags").value.trim();
/* Split tags by comma and trim each one individually */
const tags = tagsRaw
? tagsRaw.split(",").map(tag => tag.trim()).filter(tag => tag !== "")
: [];
const bookmark = {
id: Date.now().toString(),
title,
url,
tags,
createdAt: new Date().toISOString()
};
const bookmarks = BookmarkStorage.add(bookmark);
this.renderBookmarks(bookmarks);
this.form.reset();
},
handleSearch() {
const query = this.searchInput.value.toLowerCase().trim();
const bookmarks = BookmarkStorage.getAll();
if (!query) {
this.renderBookmarks(bookmarks);
return;
}
/* Filter by title or individual tags */
const filtered = bookmarks.filter(bookmark => {
const titleMatch = bookmark.title.toLowerCase().includes(query);
const tagMatch = bookmark.tags.some(tag =>
tag.toLowerCase().includes(query)
);
return titleMatch || tagMatch;
});
this.renderBookmarks(filtered);
},
renderBookmarks(bookmarks) {
if (bookmarks.length === 0) {
this.list.innerHTML = "<li class=\"bookmark-item\">No bookmarks yet. Add one above!</li>";
return;
}
this.list.innerHTML = bookmarks.map(bookmark => `
<li class="bookmark-item">
<span class="bookmark-title">${this.escapeHtml(bookmark.title)}</span>
<span class="bookmark-url">
<a href="${this.escapeHtml(bookmark.url)}" target="_blank" rel="noopener noreferrer">
${this.escapeHtml(bookmark.url)}
</a>
</span>
${bookmark.tags.length > 0 ? `
<span class="bookmark-tags">
${bookmark.tags.map(tag => `<span class="tag">${this.escapeHtml(tag)}</span>`).join("")}
</span>
` : ""}
<button class="bookmark-delete" data-id="${bookmark.id}">Delete</button>
</li>
`).join("");
/* Attach delete handlers */
this.list.querySelectorAll(".bookmark-delete").forEach(button => {
button.addEventListener("click", (e) => {
const id = e.target.dataset.id;
const updated = BookmarkStorage.remove(id);
this.renderBookmarks(updated);
});
});
},
escapeHtml(text) {
const div = document.createElement("div");
div.textContent = text;
return div.innerHTML;
}
};
/* Initialize the app */
BookmarkUI.init();
Your style.css should include styling for the #search input:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
line-height: 1.6;
padding: 2rem;
max-width: 800px;
margin: 0 auto;
background-color: #f5f5f5;
color: #333;
}
header {
margin-bottom: 2rem;
}
header h1 {
font-size: 1.8rem;
color: #2c3e50;
}
.add-bookmark {
background: #fff;
padding: 1.5rem;
border-radius: 8px;
margin-bottom: 2rem;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.add-bookmark h2 {
margin-bottom: 1rem;
font-size: 1.2rem;
}
form {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
label {
font-weight: 600;
font-size: 0.9rem;
}
input[type="text"],
input[type="url"] {
padding: 0.6rem;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
}
input[type="text"]:focus,
input[type="url"]:focus {
outline: none;
border-color: #3498db;
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);
}
button[type="submit"] {
padding: 0.7rem 1.5rem;
background-color: #3498db;
color: #fff;
border: none;
border-radius: 4px;
font-size: 1rem;
cursor: pointer;
margin-top: 0.5rem;
}
button[type="submit"]:hover {
background-color: #2980b9;
}
.bookmark-list {
background: #fff;
padding: 1.5rem;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.bookmark-list h2 {
margin-bottom: 1rem;
font-size: 1.2rem;
}
#search {
width: 100%;
padding: 0.6rem;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
margin-bottom: 1rem;
}
#search:focus {
outline: none;
border-color: #3498db;
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);
}
#bookmarks {
list-style: none;
}
.bookmark-item {
display: flex;
flex-direction: column;
gap: 0.25rem;
padding: 1rem;
border-bottom: 1px solid #eee;
}
.bookmark-item:last-child {
border-bottom: none;
}
.bookmark-title {
font-weight: 600;
color: #2c3e50;
}
.bookmark-url a {
color: #3498db;
text-decoration: none;
font-size: 0.9rem;
}
.bookmark-url a:hover {
text-decoration: underline;
}
.bookmark-tags {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
margin-top: 0.25rem;
}
.tag {
background-color: #ecf0f1;
color: #7f8c8d;
padding: 0.15rem 0.5rem;
border-radius: 3px;
font-size: 0.8rem;
}
.bookmark-delete {
background: none;
border: none;
color: #e74c3c;
cursor: pointer;
font-size: 0.85rem;
align-self: flex-end;
padding: 0.25rem;
}
.bookmark-delete:hover {
text-decoration: underline;
}
You now have a project that remembers your coding preferences and a search feature that filters bookmarks instantly. Next up, you'll practice debugging and refactoring with Claude Code to polish the app further.
Debug, Refactor, and Manage Your Project
You have a working bookmark manager with search functionality. Now it's time to learn how Claude Code handles the day-to-day work of maintaining a project: finding bugs, cleaning up code, and keeping your git history tidy.
These three workflows (debugging, refactoring, and committing) are what you'll use Claude Code for most often after the initial build. In this step, you'll practice all three by describing problems and goals in plain English.
In this step, get ready to:
- Debug a tag search issue by describing the problem to Claude Code.
- Refactor your app's code structure using CLAUDE.md conventions.
- Commit changes and review your full git history through conversation.
Practice debugging with Claude Code
One of Claude Code's most practical skills is tracking down bugs from a plain-English description. You don't need to know the exact line that's broken. You just describe the symptom, and Claude Code traces the cause across your files.
- In your Claude Code session, describe the tag search bug by typing this prompt:
there's a problem - when I add a bookmark with multiple tags separated by commas, the search doesn't find bookmarks by individual tags. Fix this bug
What does Claude Code do with this?
Claude Code reads through your codebase, locates the search logic in app.js, and traces how tags are stored vs. how they're being searched. It then explains the root cause and proposes a fix.
You'll see Claude Code show you exactly which lines it's changing and why. This is the debugging pattern: describe the symptom, let Claude Code find the cause.
- Review the proposed changes Claude Code shows you.
- Approve the fix when prompted.
- Open index.html in your browser and test the fix by adding a bookmark with multiple tags (e.g., dev, tools, reference), then searching for just one of those tags.
Search still not filtering by individual tags?
If the fix didn't work as expected, tell Claude Code what you're seeing. Type something like: I tried searching for 'tools' but it's not showing my bookmark that has 'dev, tools, reference' as tags. Claude Code will investigate further.
Help me debug why tag search isn't working in my bookmark manager
Ask Claude Code to refactor your code
Refactoring means restructuring code without changing what it does. This is where your CLAUDE.md file pays off. Claude Code reads your stated conventions and applies them during the refactor.
- Ask Claude Code to refactor by typing this prompt:
refactor app.js to separate concerns - put storage logic in a separate module pattern and keep UI logic separate. Follow the conventions in CLAUDE.md
What happens during the refactor?
Claude Code reads your CLAUDE.md and applies the conventions you defined: small well-named functions, comments on complex logic, and the module pattern.
You'll see it reorganize app.js into a BookmarkStorage module (handling localStorage operations) and a BookmarkUI module (handling rendering and user interaction). This is the power of CLAUDE.md: your preferences are applied automatically without repeating them every time.
- Review the proposed changes and approve them.
- Refresh index.html in your browser and confirm the app still works the same way. Add a bookmark, search for it, and delete it.
Why refactor if it already works?
Separating storage from UI makes each piece easier to understand and change independently. If you later swap localStorage for a real database, you only touch BookmarkStorage. The UI code stays untouched.
App broken after refactoring?
If bookmarks don't load or the form doesn't work after the refactor, tell Claude Code exactly what's broken. Type something like: after the refactor, clicking Save Bookmark doesn't add anything to the list. Claude Code will trace the issue back to the refactored code.
Help me fix my app after Claude Code refactored it
Commit your changes and review project history
Claude Code's Git integration lets you stage, commit, and review history without leaving the conversation. It writes descriptive commit messages based on what actually changed.
- Commit your bug fix and refactor by typing this prompt:
commit these changes, then show me all the commits we've made today
What does Claude Code do here?
Claude Code stages your modified files, writes a descriptive commit message summarizing the bug fix and refactor, and then runs git log to show your full project history. You should see three commits with clear messages describing what each one accomplished.
- Confirm the git log shows three commits with descriptive messages for all the work you've done across the project.
Seeing fewer than three commits?
If your git log shows fewer commits, some changes may not have been committed in earlier steps. Type show me the git status and commit any uncommitted changes to catch up. Claude Code will check for any staged or unstaged changes and commit them.
Help me fix my git history in Claude Code
You now have a clean project with well-organized code and a meaningful git history. All of it created through conversation. Next up, you'll tackle the Secret Mission and push Claude Code's capabilities even further with dark mode and CSS custom properties.
Secret mission
Dark Mode with CSS Custom Properties
Your bookmark manager works great, but it only has one look. What if you could add a dark mode toggle and refactor the entire stylesheet to use CSS custom properties? This mission teaches you how to use Claude Code's plan mode to review an approach before any files change.
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely locally with no cloud resources or ongoing costs. Your Claude Pro or Max subscription is a separate, pre-existing expense that is not affected by this project.
Decide whether to keep your resources running, pause them to come back later, or delete them entirely.
Resources you used:
- The bookmark-manager project folder on your Desktop (contains index.html, style.css, app.js, CLAUDE.md, and git history).
- Claude Code CLI installed via the native installer.
- Claude Code configuration and session data stored in ~/.claude and ~/.claude.json.
Keep everything running
No action needed. Choose this if you plan to keep using Claude Code and want to continue building on your bookmark manager.
- Your bookmark-manager folder stays on your Desktop, ready to open in a browser or extend with Claude Code.
- Claude Code stays installed and authenticated for your next project.
- Your Claude Code session history and settings persist in ~/.claude.
Pause - I'll come back to this later
Close any running processes to free up terminal space. Your files and configuration stay intact for when you return.
- If Claude Code is still running in your terminal, type exit or press Ctrl+D to end the session.
- Close the terminal window.
- When you return, navigate back to ~/Desktop/bookmark-manager and run claude to pick up where you left off.
Delete - I don't want to use this again
Remove all project files and optionally uninstall Claude Code from your system.
- If Claude Code is still running, type exit or press Ctrl+D to end the session.
- Delete the project folder by running these commands:
cd ~/Desktop
rm -rf bookmark-manager
- To uninstall Claude Code (native installation), remove the binary and version files:
rm -f ~/.local/bin/claude
rm -rf ~/.local/share/claude
- To remove Claude Code configuration and session history:
rm -rf ~/.claude
rm ~/.claude.json
What about my Claude Pro subscription?
Uninstalling Claude Code does not cancel your Claude Pro or Max subscription. If you want to manage your subscription, visit your account settings at claude.ai.
Nice Work!
Nice Work!
You just built a fully functional web app without writing a single line of code manually. Through natural language conversations with Claude Code, you went from an empty folder to a working bookmark manager with search, tags, and persistent storage.
You learned how to:
- Installed and authenticated Claude Code, then built a complete bookmark manager app through natural language conversations.
- Created a CLAUDE.md file that customizes Claude Code's behavior and enforces your project's coding standards.
- Used Claude Code for debugging, refactoring, and git workflows to manage your project history through conversation.
- Added dark mode with CSS custom properties using Claude Code's plan mode to review changes before execution.
Ready to quiz yourself?