Svelte 50 Projects 50 Days - Project 1 Expanding Card
Published: 2021-10-23 03:30:30

Learning a new JavaScript framework [50projects50days](https://github.com/bradtraversy/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](https://50projects50days.com/projects/expanding-cards/) in Svelte. For this project, we can use [Svelte REPL](https://svelte.dev/repl/hello-world?version=3). Here we can do live code. So let's start.

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

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

<!-- HTML content goes here -->

<style>
/* style goes here */
</style>
```

So let's implement the `template` part.

##Making Template

```html
<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>
```

And the `style` part.

```css
<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>
```
And it looks like this.

![card-image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kuqzw0wble6kyd29gcb6.png)

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.

```html
  <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>

```
In Style Part
```css
  .panel {
    flex: 0.5; //changed 1 to 0.5;
  }
  .panel.active {
    flex: 5;
  }

  .panel.active h3 {
    opacity: 1;
  }
```
Now, its look like this.

![Images](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wb2scgfug6tfjvfkivcz.png)

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:

```html
<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>
```

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 `0`th 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:

```js
  let panel_container;

  function handleActiveClasses(i) {
    if(panel_container){
      panel_container.childNodes[i].classList.add('active')
    }
  }
``` 

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:

```html
<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>
```

Now it looks like this. 

![Expand Image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fkzer7et9cnz0yf3ccop.gif)

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:

```css
  .panel{
    -webkit-transition: all 700ms ease-in;
  }
  .panel.active h3 {
    transition: opacity 0.3s ease-in 0.4s;
  }
```
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:

```js
function handleActiveClasses(i) {
  if(panel_container){
    panel_container.childNodes.forEach(panel => {
      panel.classList.remove('active')
    })
    panel_container.childNodes[i].classList.add('active')
  }
}
```
The final output is:

![Final output](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/19v44211uvcsw9uxrwde.gif)


We finish our first project in Svelte.

##Summary

In this project this Svelte features we used:

[adding Data](https://svelte.dev/tutorial/adding-data)
[styling](https://svelte.dev/tutorial/styling)
[each](https://svelte.dev/tutorial/each-blocks)
[dom-events](https://svelte.dev/tutorial/dom-events)
[inline-handlers](https://svelte.dev/tutorial/inline-handlers)
[reference html elements](https://svelte.dev/tutorial/bind-this)


You can find the final code in this [REPL](https://svelte.dev/repl/ae732fe0924f4d788e7c36bd9fee8ebf?version=3.44.0).

GitHub profile: [GitHub](https://github.com/msisaifu).