Reputation: 101
I am building a page with three card components. One of the three cards (the middle one) is to be styled differently to the other two, and I can't get that to happen. Here's what I have so far...
Card.svelte (below)
<script>
export let subLevel;
export let price;
export let users;
export let storage;
export let sendLimit;
import Button from "./Button.svelte";
</script>
<div class="card">
<h2>{subLevel}</h2>
<ul>
<li class="price"><span class="dollar">$</span>{price}</li>
<li class="spec">{storage} Storage</li>
<li class="spec">{users} Users Allowed</li>
<li class="spec">Send up to {sendLimit} GB</li>
</ul>
<Button>Learn more</Button>
</div>
<style>
.card {
display: flex;
flex-flow: column nowrap;
align-items: center;
padding-top: 2rem;
background-color: #fff;
border-radius: 10px;
max-width: 350px;
color: var(--gray-blue);
padding-inline: 29px;
}
.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(--dark-gray-blue);
}
ul {
width: 100%;
list-style: none;
text-align: center;
}
li {
text-decoration: none;
border-top: 1px solid var(--gray-blue);
padding-block: 13px;
}
li:last-child {
border-bottom: 1px solid var(--gray-blue);
}
</style>
Button.svelte (below)
<button><slot /></button>
<style>
button {
text-transform: uppercase;
width: 100%;
background: linear-gradient(135deg, #a2a7f0, #696edd);
color: #fff;
font-size: 0.8125rem;
letter-spacing: 1.39px;
padding-block: 0.875rem;
border: none;
border-radius: 6px;
cursor: pointer;
margin-block: 2rem;
}
</style>
App.svelte (below)
<script>
import Card from "./components/Card.svelte";
import Footer from "./components/Footer.svelte";
// Card details
const basicMonthly = {
subLevel: "Basic",
price: 19.99,
storage: "500 GB",
users: 2,
sendLimit: 3,
};
const proMonthly = {
subLevel: "Professional",
price: 24.99,
storage: "1 TB",
users: 5,
sendLimit: 10,
};
const masterMonthly = {
subLevel: "Master",
price: 39.99,
storage: "2 TB",
users: 10,
sendLimit: 20,
};
</script>
<main class="site-content">
<Card {...basicMonthly} />
<Card {...proMonthly} />
<Card {...masterMonthly} />
</main>
<Footer />
<style>
</style>
My question is: how can I add unique styling to the second Card component? It should have the following styles:
Styling for middle component (below)
<style>
.card {
display: flex;
flex-flow: column nowrap;
align-items: center;
padding-top: 2rem;
background-color: linear-gradient(135deg, #a2a7f0, #696edd);
border-radius: 10px;
max-width: 350px;
color: #fff;
padding-inline: 29px;
}
.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: #fff;
}
ul {
width: 100%;
list-style: none;
text-align: center;
}
li {
text-decoration: none;
border-top: 1px solid #fff;
padding-block: 13px;
}
li:last-child {
border-bottom: 1px solid #fff;
}
</style>
... and Button.svelte has to change on that middle component, too. It should be styled as follows:
<style>
button {
text-transform: uppercase;
width: 100%;
background: #fff;
color: #6d72de;
font-size: 0.8125rem;
letter-spacing: 1.39px;
padding-block: 0.875rem;
border: none;
border-radius: 6px;
cursor: pointer;
margin-block: 2rem;
}
</style>
I'm new to Svelte, and have been completely stumped by this. I've tried using $$restProps
and dynamic styling, but I can't seem to dig through to the button and the various elements nested in the card div
that I need to.
Is this possible with Svelte, or should I simply build another component to suit what I need? Surely there's another (drier) way to do this? Any help greatly appreciated!
Upvotes: 2
Views: 2046
Reputation: 5472
There's a bunch of different ways to tackle this, but I would do something like the following:
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.
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>
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.
Upvotes: 1