Multiple instances of same component, one with unique style?

Solution 1:

There's a bunch of different ways to tackle this, but I would do something like the following:

  1. Introduce a boolean prop called "secondary" into the Card and Button components. This will default to false, but will be set to true for the middle card. The controls whether we apply the alternate styles. The Card will pass it into the Button component, so that if secondary = true for the Card, it will also equal true for the Button.

    export let secondary = false;
    
    <!-- App.svelte -->
    <main class="site-content">
      <Card {...basicMonthly} />
      <Card secondary {...proMonthly} />
      <Card {...masterMonthly} />
    </main>
    
    <!-- Card.svelte -->
    <!-- ... rest of template -->
    <Button {secondary}>Learn more</Button>
    

    If you end up having multiple different variants, it might make sense to make this a string instead, e.g. variant="secondary", variant="tertiary", etc.

  2. Add the class "secondary" to the Card and Button based on whether the secondary prop is true. Svelte's class directive makes this easy.

    <!-- Card.svelte -->
    <div class="card" class:secondary>
    
    <!-- Button.svelte -->
    <button class:secondary><slot /></button>
    
  3. Apply the following styles. While you pasted a lot of different styles for the second Card, the only thing that is changing is the background and text colors. I moved these values into CSS custom properties. When the secondary class is applied, the custom properties are updated and everything else uses the new colors.

    <!-- Card.svelte styles -->
    <style>
      .card {
        --background: #fff;
        --text-color: var(--gray-blue);
    
        display: flex;
        flex-flow: column nowrap;
        align-items: center;
        padding-top: 2rem;
        background: var(--background);
        border-radius: 10px;
        max-width: 350px;
        color: var(--text-color);
        padding-inline: 29px;
      }
    
      .card.secondary {
        --background: linear-gradient(135deg, #a2a7f0, #696edd);
        --text-color: #fff;
      }
    
      .dollar {
        font-size: 2.5rem;
        margin-right: 0.5rem;
      }
    
      .price {
        display: flex;
        font-size: 4.5rem;
        align-items: center;
        justify-content: center;
        border-top: none;
        color: var(--text-color);
      }
    
      ul {
        width: 100%;
        list-style: none;
        text-align: center;
      }
    
      li {
        text-decoration: none;
        border-top: 1px solid var(--text-color);
        padding-block: 13px;
      }
    
      li:last-child {
        border-bottom: 1px solid var(--text-color);
      }
    </style>
    
    <!-- Button.svelte styles -->
    <style>
      button {
        --background: linear-gradient(135deg, #a2a7f0, #696edd);
        --text-color: #fff;
    
        text-transform: uppercase;
        width: 100%;
        background: var(--background);
        color: var(--text-color);
        font-size: 0.8125rem;
        letter-spacing: 1.39px;
        padding-block: 0.875rem;
        border: none;
        border-radius: 6px;
        cursor: pointer;
        margin-block: 2rem;
      }
    
      button.secondary {
        --background: #fff;
        --text-color: #6d72de;
      }
    </style>
    

With these updates, there is no duplication and you don't have to create separate components. You can see the final demo in this Svelte REPL.