This is the second post of Building a Minimalist Task Manager series

Welcome back to our vanilla JavaScript task manager series! In the last post, we built a solid foundation with basic add/delete functionality. Today, we’re adding the star of the show: the three-task limit that gives our app its personality and purpose.

But we’re not just slapping on a simple if (tasks.length >= 3) check and calling it a day. We’re going to make this limitation feel intentional and smooth, with visual feedback that guides users rather than frustrating them.

Why Three Tasks?

Before we dive into code, let’s talk about why this limitation exists. The idea is simple: when you have too many tasks staring at you, decision paralysis kicks in. By limiting yourself to just three active tasks, you’re forced to:

  • Prioritize what actually matters today
  • Finish things before starting new ones
  • Avoid the endless scroll of doom that most todo apps become

Think of it as Marie Kondo for your productivity - if it doesn’t spark immediate action, it doesn’t get to stay on the list.

What We’re Adding Today

  • The 3-task limit: Users can only have 3 active tasks maximum
  • Task completion: Mark tasks as done instead of just deleting them
  • Smart UI feedback: Visual cues when you hit the limit
  • Better task management: Completed tasks get their own section

Let’s dive in and make our task manager actually enforce some digital discipline!

Updating Our HTML Structure

First, let’s modify our HTML to accommodate completed tasks and better visual feedback. Here are the changes to the task list container:

<!-- Replace the existing task-list-container div with this: -->
<div class="task-list-container">
    <div class="active-tasks-section">
        <h3>Active Tasks <span id="taskCounter">(0/3)</span></h3>
        <ul id="taskList" class="task-list">
            <!-- Active tasks will be dynamically added here -->
        </ul>
        <p id="limitMessage" class="limit-message" style="display: none;">
            Focus mode activated! Complete a task to add more.
        </p>
    </div>
    
    <div class="completed-tasks-section" id="completedSection" style="display: none;">
        <h3>Completed Tasks</h3>
        <ul id="completedTaskList" class="task-list completed-list">
            <!-- Completed tasks will be dynamically added here -->
        </ul>
    </div>
</div>

Enhanced CSS Styling

Let’s add some new CSS rules to handle our enhanced UI. Add these to your existing styles.css:

/* Add these new styles to your existing styles.css */

.active-tasks-section h3, .completed-tasks-section h3 {
    margin-bottom: 1rem;
    color: #2d3748;
    font-size: 1.2rem;
    font-weight: 500;
}

#taskCounter {
    font-weight: 300;
    color: #718096;
}

.limit-message {
    text-align: center;
    color: #e53e3e;
    font-style: italic;
    margin: 1rem 0;
    padding: 0.75rem;
    background-color: #fed7d7;
    border-radius: 6px;
}

.completed-tasks-section {
    margin-top: 2rem;
    padding-top: 2rem;
    border-top: 1px solid #e2e8f0;
}

.completed-list .task-item {
    opacity: 0.7;
    background-color: #f7fafc;
}

.completed-list .task-text {
    text-decoration: line-through;
    color: #718096;
}

.complete-btn {
    background-color: #48bb78;
    color: white;
    border: none;
    padding: 0.25rem 0.5rem;
    border-radius: 4px;
    cursor: pointer;
    font-size: 0.875rem;
    margin-right: 0.5rem;
}

.complete-btn:hover {
    background-color: #38a169;
}

/* Update the existing #addTaskBtn to show disabled state */
#addTaskBtn:disabled {
    background-color: #a0aec0;
    cursor: not-allowed;
}

#addTaskBtn:disabled:hover {
    background-color: #a0aec0;
}

The Enhanced JavaScript Logic

Now for the main event - let’s update our JavaScript to implement the focus mode. Here’s the enhanced script.js:

// Get references to our DOM elements
const taskInput = document.getElementById('taskInput');
const addTaskBtn = document.getElementById('addTaskBtn');
const taskList = document.getElementById('taskList');
const completedTaskList = document.getElementById('completedTaskList');
const completedSection = document.getElementById('completedSection');
const taskCounter = document.getElementById('taskCounter');
const limitMessage = document.getElementById('limitMessage');

// Constants
const MAX_ACTIVE_TASKS = 3;

// Arrays to store our tasks
let activeTasks = [];
let completedTasks = [];

// Function to update the task counter and UI state
function updateUI() {
    // Update counter
    taskCounter.textContent = `(${activeTasks.length}/${MAX_ACTIVE_TASKS})`;
    
    // Show/hide limit message and disable button if at limit
    const atLimit = activeTasks.length >= MAX_ACTIVE_TASKS;
    limitMessage.style.display = atLimit ? 'block' : 'none';
    addTaskBtn.disabled = atLimit;
    
    // Show/hide completed section
    completedSection.style.display = completedTasks.length > 0 ? 'block' : 'none';
    
    console.log(`Active: ${activeTasks.length}, Completed: ${completedTasks.length}`);
}

// Function to create a new active task element
function createActiveTaskElement(taskText, taskId) {
    const li = document.createElement('li');
    li.className = 'task-item';
    li.innerHTML = `
        <span class="task-text">${taskText}</span>
        <div>
            <button class="complete-btn" onclick="completeTask(${taskId})">Done</button>
            <button class="delete-btn" onclick="deleteTask(${taskId})">Delete</button>
        </div>
    `;
    return li;
}

// Function to create a completed task element
function createCompletedTaskElement(taskText, taskId) {
    const li = document.createElement('li');
    li.className = 'task-item';
    li.innerHTML = `
        <span class="task-text">${taskText}</span>
        <button class="delete-btn" onclick="deleteCompletedTask(${taskId})">Remove</button>
    `;
    return li;
}

// Function to add a new task
function addTask() {
    const taskText = taskInput.value.trim();
    
    // Check if input is empty
    if (taskText === '') {
        alert('Please enter a task!');
        return;
    }
    
    // Check if we're at the limit
    if (activeTasks.length >= MAX_ACTIVE_TASKS) {
        alert(`Focus mode! You can only have ${MAX_ACTIVE_TASKS} active tasks. Complete one first.`);
        return;
    }
    
    // Create task object
    const task = {
        id: Date.now(),
        text: taskText,
        createdAt: new Date()
    };
    
    // Add to active tasks array
    activeTasks.push(task);
    
    // Create and add the task element to the DOM
    const taskElement = createActiveTaskElement(task.text, task.id);
    taskList.appendChild(taskElement);
    
    // Clear the input field
    taskInput.value = '';
    
    // Update UI state
    updateUI();
}

// Function to complete a task (move from active to completed)
function completeTask(taskId) {
    // Find the task in active tasks
    const taskIndex = activeTasks.findIndex(task => task.id === taskId);
    if (taskIndex === -1) return;
    
    const task = activeTasks[taskIndex];
    
    // Move to completed tasks
    task.completedAt = new Date();
    completedTasks.push(task);
    activeTasks.splice(taskIndex, 1);
    
    // Remove from active DOM and add to completed DOM
    removeTaskElementById(taskList, taskId);
    const completedElement = createCompletedTaskElement(task.text, task.id);
    completedTaskList.appendChild(completedElement);
    
    // Update UI state
    updateUI();
}

// Function to delete an active task
function deleteTask(taskId) {
    // Remove from active tasks array
    activeTasks = activeTasks.filter(task => task.id !== taskId);
    
    // Remove from DOM
    removeTaskElementById(taskList, taskId);
    
    // Update UI state
    updateUI();
}

// Function to delete a completed task
function deleteCompletedTask(taskId) {
    // Remove from completed tasks array
    completedTasks = completedTasks.filter(task => task.id !== taskId);
    
    // Remove from DOM
    removeTaskElementById(completedTaskList, taskId);
    
    // Update UI state
    updateUI();
}

// Helper function to remove task element by ID
function removeTaskElementById(parentElement, taskId) {
    const taskElements = parentElement.children;
    for (let i = 0; i < taskElements.length; i++) {
        const buttons = taskElements[i].querySelectorAll('button');
        for (let button of buttons) {
            if (button.onclick && button.onclick.toString().includes(taskId)) {
                parentElement.removeChild(taskElements[i]);
                return;
            }
        }
    }
}

// Event listeners
addTaskBtn.addEventListener('click', addTask);

// Allow adding tasks by pressing Enter (but respect the limit)
taskInput.addEventListener('keypress', function(e) {
    if (e.key === 'Enter') {
        addTask();
    }
});

// Initialize UI on page load
updateUI();

What’s New and Improved

The magic happens in several key areas:

  1. Task Limit Enforcement: The addTask() function now checks if we’re at the 3-task limit before allowing new tasks.

  2. Smart UI Updates: The updateUI() function handles the counter, button states, and visual feedback all in one place.

  3. Task Completion: Instead of just deleting tasks, we can now mark them as done, which moves them to a separate completed section.

  4. Visual Feedback: When you hit the 3-task limit, the add button gets disabled and a friendly message appears.

  5. Better Organization: Active and completed tasks are clearly separated, making it easy to see your progress.

The Psychology Behind the Magic

Here’s why this 3-task limit is actually brilliant:

  • Reduces decision paralysis: Instead of staring at a massive list, you focus on just 3 things
  • Creates urgency: Limited slots make you prioritize what really matters
  • Builds momentum: Completing tasks feels more rewarding when you can immediately add something new
  • Prevents overwhelm: Your brain can actually handle 3 priorities at once

Try It Out!

Go ahead and test the enhanced version:

  1. Add your first task - notice the counter updates
  2. Add two more tasks - you’ll see you’re at the limit
  3. Try to add a fourth task - the app politely refuses
  4. Mark a task as “Done” - watch it move to the completed section
  5. Notice how the add button becomes available again

Pretty satisfying, right?

What’s Coming Next

In our next post, we’ll add localStorage to persist your tasks between browser sessions (because losing your carefully curated 3-task list would be tragic), and we might even add some task reordering functionality.

The focus mode is working, but we’re just getting started. Next time, we’ll make sure your digital discipline survives browser refreshes!

Keep coding, and remember - less is more!