Creating Interactive Task Cards

Creating Interactive Task Cards

In our previous lesson, we transformed static column placeholders into dynamic, reusable Column components. Now that we have this flexible container structure in place, it's time to populate these columns with their core content: individual task cards.

Task cards are the building blocks of any Kanban system. Each card represents a single work item that can move between columns as work progresses. A well-designed task card should communicate key information at a glance while being interactive and accessible. In this lesson, we'll create cards that display a title and an optional description, with visual feedback when hovered over or focused.

Building The TaskCard Component

Let's create a new file called TaskCard.svelte in our components directory. This component will represent each individual task in our Kanban board.

Defining Component Props

We'll start by defining the component's props using Svelte 5's $props() function, which you'll remember from our work with the Column component. The key difference here is that we're making the description optional by providing a default empty string value:

<script>
  let { title, description = '' } = $props();
</script>

Building The Component Template

The template portion needs to handle two scenarios: when a description exists and when it doesn't. We'll use Svelte's {#if} block for conditional rendering:

<div class="task-card" tabindex="0">
  <h3>{title}</h3>
  {#if description}
    <p class="description">{description}</p>
  {/if}
</div>

The {#if} block ensures the description paragraph only appears when a description is provided.

Notice we've included tabindex="0" to make the card keyboard-navigable. This is an important accessibility feature that allows users to interact with our cards using keyboard navigation.

Implementing Interactive Styling

For styling, we'll implement several important interaction states:

<style>
  .task-card {
    background: white;
    padding: 0.75rem;
    border-radius: 6px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
    margin-bottom: 0.5rem;
    transition: transform 0.15s ease, box-shadow 0.15s ease;
    cursor: pointer;
  }

  .task-card:hover, .task-card:focus {
    transform: translateY(-2px);
    box-shadow: 0 3px 6px rgba(0, 0, 0, 0.15);
  }

  .task-card:focus {
    outline: 2px solid #4299e1;
    outline-offset: 2px;
  }

  h3 {
    margin-top: 0;
    margin-bottom: 0.5rem;
    font-size: 1rem;
  }

  .description {
    margin: 0;
    font-size: 0.875rem;
    color: #4a5568;
  }
</style>

The transition property creates smooth animations when the card moves, while the :hover and :focus pseudo-classes provide clear visual feedback to users.

Understanding Reactive Design Principles

Our TaskCard component embodies several key reactive programming principles:

  1. Data as the Single Source of Truth: The props we define (title and description) are our component's data model, and the UI is derived from this data.

  2. Declarative Rendering: We declare that the description paragraph should only appear when a description exists, using the {#if} block.

  3. Automatic UI Updates: If the description prop changes, our UI will automatically update to show or hide the paragraph without any manual DOM manipulation.

This reactive approach means our component will consistently reflect its current data state, making the UI predictable and easier to reason about.

Populating Columns With Tasks

Now that we have our TaskCard component, let's integrate it with the Column component we built in the previous lesson. The @render children?.() pattern we implemented in the Column component will automatically handle our TaskCards:

<script>
  import Column from './Column.svelte';
  import TaskCard from './TaskCard.svelte';
</script>

<div class="board">
  <Column title="To Do">
    <TaskCard title="Learn Svelte 5" description="Study the new Runes API" />
    <TaskCard title="Design components" />
  </Column>
  <Column title="In Progress">
    <TaskCard title="Build Kanban board" description="Create the main layout" />
  </Column>
  <Column title="Done">
    <TaskCard title="Setup project" description="Initialize SvelteKit" />
  </Column>
</div>

This implementation will render three columns with sample tasks. The first column shows two tasks (one with a description, one without), demonstrating both use cases of our conditional rendering. The responsive behavior we implemented earlier remains intact — columns will stack vertically on smaller screens while maintaining all task card styling and interactions.

Summary & Practice Prep

In this lesson, we've completed a crucial part of our Kanban board by implementing interactive TaskCard components. You've learned how to create components with optional props, conditionally render content, and implement accessible interactions. These skills form the foundation for the more advanced features we'll add in future lessons.

The practice exercises will help you solidify these concepts by:

  • Creating TaskCards with different combinations of titles and descriptions
  • Customizing the visual appearance of cards (colors, spacing, shadows)
  • Testing keyboard navigation between cards
  • Experimenting with adding new task types

Remember that while our current implementation is static, the component patterns we've established will work seamlessly when we later connect to dynamic data sources. The separation between presentation (TaskCard) and container (Column) components will prove especially valuable as our application grows in complexity.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal