DEV Community

Mohammad Saiful Islam
Mohammad Saiful Islam

Posted on

Svelte 50 Projects 50 Days - Project 1 Expanding Card

Learning a new JavaScript framework 50projects50days is a great resource. We can convert them to the new framework. Nowadays Svelte is one of the hot UI frameworks (actually it's a build-time compiler). I decided I convert those projects to Svelte. So I'm going to make our first project Expanding Cards in Svelte. For this project, we can use Svelte REPL. Here we can do live code. So let's start.

A Svelte component has 3 parts. script, template, style.

<script>
  //JavaScipt logic goes here
</script>

<!-- HTML content goes here -->

<style>
/* style goes here */
</style>
Enter fullscreen mode Exit fullscreen mode

So let's implement the template part.

Making Template

<main>
  <div class="container">
    <div class="panel" style="background-image: url('https://images.unsplash.com/photo-1558979158-65a1eaa08691?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80')">
      <h3>Explore The World</h3>
    </div>
  </div>
</main>
Enter fullscreen mode Exit fullscreen mode

And the style part.

<style>
  @import url('https://fonts.googleapis.com/css?family=Muli&display=swap');

  * {
    box-sizing: border-box;
  }
  main {
    font-family: 'Muli', sans-serif;
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100vh;
    overflow: hidden;
    margin: 0;
  }

  .container {
    display: flex;
    width: 90vw;
  }

  .panel {
    height: 80vh;
    border-radius: 25px;
    color: #fff;
    flex: 1;
    cursor: pointer;
    margin: 10px;
    position: relative;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
  }

  .panel h3 {
    font-size: 24px;
    position: absolute;
    bottom: 20px;
    left: 20px;
    margin: 0;
    opacity: 0;
  }

</style>
Enter fullscreen mode Exit fullscreen mode

And it looks like this.

card-image

Now we are going to add 5 photos in the container and the first panel should have an active class, and also for the active class add some CSS.

  <div class="panel active" style="background-image: url('https://images.unsplash.com/photo-1558979158-65a1eaa08691?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80')">
    <h3>Explore The World</h3>
  </div>
  <div class="panel" style="background-image: url('https://images.unsplash.com/photo-1572276596237-5db2c3e16c5d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80')">
    <h3>Wild Forest</h3>
  </div>
  <div class="panel" style="background-image: url('https://images.unsplash.com/photo-1507525428034-b723cf961d3e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1353&q=80')">
    <h3>Sunny Beach</h3>
  </div>
  <div class="panel" style="background-image: url('https://images.unsplash.com/photo-1551009175-8a68da93d5f9?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1351&q=80')">
    <h3>City on Winter</h3>
  </div>
  <div class="panel" style="background-image: url('https://images.unsplash.com/photo-1549880338-65ddcdfd017b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80')">
    <h3>Mountains - Clouds</h3>
  </div>

Enter fullscreen mode Exit fullscreen mode

In Style Part

  .panel {
    flex: 0.5; //changed 1 to 0.5;
  }
  .panel.active {
    flex: 5;
  }

  .panel.active h3 {
    opacity: 1;
  }
Enter fullscreen mode Exit fullscreen mode

Now, its look like this.

Images

Now our template is ready, we need to implement the logic for the click event on a panel its need to expand and the rest of are shrink.

Implementing Logic

Here we write 5 hardcoded panel blocks, but in Svelte we can simplify them. For instance:

<script>
  const images = [
    { src: 'https://images.unsplash.com/photo-1558979158-65a1eaa08691?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80', caption: 'Explore The World'},
    { src: 'https://images.unsplash.com/photo-1572276596237-5db2c3e16c5d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80', caption: 'Wild Forest'},
    { src: 'https://images.unsplash.com/photo-1507525428034-b723cf961d3e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1353&q=80', caption: 'Sunny Beach'},
    { src: 'https://images.unsplash.com/photo-1551009175-8a68da93d5f9?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1351&q=80', caption: 'City on Winter'},
    { src: 'https://images.unsplash.com/photo-1549880338-65ddcdfd017b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80', caption: 'Mountains - Clouds'}
  ]
</script>

<main>
  <div class="container">
    {#each images as {src, caption}, i}
      <div class="panel {i == 0 ? 'active' : ''}" style="background-image: url({src})">
        <h3>{caption}</h3>
      </div>
    {/each}
  </div>
</main>
Enter fullscreen mode Exit fullscreen mode

Here we declare an image list and we render it by each block, each has two arguments first one the item and the next one the index, also in the template, we render a class conditionally, so for the 0th position add an active class.

Our next target is we need to add a click event and need to reference the container elements. First, let's do it in the script:

  let panel_container;

  function handleActiveClasses(i) {
    if(panel_container){
      panel_container.childNodes[i].classList.add('active')
    }
  }
Enter fullscreen mode Exit fullscreen mode

In template for reference an element we use bind:this={panel_container} and add attach the event handler which takes a arguments i as an index:

<div class="container" bind:this={panel_container}>
  {#each images as {src, caption}, i}
    <div on:click={() => handleActiveClasses(i)} class="panel {i == 0 ? 'active' : ''}" style="background-image: url({src})">
      <h3>{caption}</h3>
    </div>
  {/each}
</div>
Enter fullscreen mode Exit fullscreen mode

Now it looks like this.

Expand Image

Here still has two problems, first, one is expanded only targeted panel and need to rest are shrinking, but we see every panel are expanded for click. And the second problem is the expanding panel is looking weird, so we can add a transition. first, let's add the transition:

  .panel{
    -webkit-transition: all 700ms ease-in;
  }
  .panel.active h3 {
    transition: opacity 0.3s ease-in 0.4s;
  }
Enter fullscreen mode Exit fullscreen mode

To shrink the panel we need to remove the active class from every element and add an active class for the current index. Let's update our function:

function handleActiveClasses(i) {
  if(panel_container){
    panel_container.childNodes.forEach(panel => {
      panel.classList.remove('active')
    })
    panel_container.childNodes[i].classList.add('active')
  }
}
Enter fullscreen mode Exit fullscreen mode

The final output is:

Final output

We finish our first project in Svelte.

Summary

In this project this Svelte features we used:

adding Data
styling
each
dom-events
inline-handlers
reference html elements

You can find the final code in this REPL.

GitHub profile: GitHub.

Discussion (0)