Help with math in SCSS

Hey, I'm currently working on this project Right now what bothers me is the top-right corner, AKA the theme selection My current Svelte code:
<div class="tri-switch">
{#each themes as themeCurrent, i}
<input
id="theme-{themeCurrent}"
name="theme-switch"
class="theme-{i}"
checked={$theme === themeCurrent}
value={themeCurrent}
type="radio"
bind:group={$theme}/>
{/each}

<div class="switch-labels">
<label for="theme-auto">
<ThemeAuto checked={$theme === "auto"}/>
</label>
<label for="theme-light">
<ThemeLight checked={$theme === "light"}/>
</label>
<label for="theme-dark">
<ThemeDark checked={$theme === "dark"}/>
</label>
</div>

<div class="switch-handle"></div>
</div>

<style lang="scss">
<div class="tri-switch">
{#each themes as themeCurrent, i}
<input
id="theme-{themeCurrent}"
name="theme-switch"
class="theme-{i}"
checked={$theme === themeCurrent}
value={themeCurrent}
type="radio"
bind:group={$theme}/>
{/each}

<div class="switch-labels">
<label for="theme-auto">
<ThemeAuto checked={$theme === "auto"}/>
</label>
<label for="theme-light">
<ThemeLight checked={$theme === "light"}/>
</label>
<label for="theme-dark">
<ThemeDark checked={$theme === "dark"}/>
</label>
</div>

<div class="switch-handle"></div>
</div>

<style lang="scss">
.tri-switch {
display: flex;
align-items: center;
position: relative;
}

input[type="radio"] {
display: none;
}

.switch-labels {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.62rem;

label {
flex: 1;
width: 30px;
aspect-ratio: 1;
cursor: pointer;
display: flex;
align-items: center;
}
}

$option-count: 3;
$handle-width: calc(100% / $option-count);
$portion: 100% / $option-count;

.switch-handle {
position: absolute;
z-index: -1;
width: calc(100% / $option-count);
aspect-ratio: 1;
border-radius: 50%;
background: var(--switch-theme-thumb-color);
pointer-events: none;
}

@for $i from 0 to $option-count {
.theme-#{$i}:checked ~ .switch-handle {
left: calc(#{$i} * $portion + ($portion - $handle-width));
}
}
.tri-switch {
display: flex;
align-items: center;
position: relative;
}

input[type="radio"] {
display: none;
}

.switch-labels {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.62rem;

label {
flex: 1;
width: 30px;
aspect-ratio: 1;
cursor: pointer;
display: flex;
align-items: center;
}
}

$option-count: 3;
$handle-width: calc(100% / $option-count);
$portion: 100% / $option-count;

.switch-handle {
position: absolute;
z-index: -1;
width: calc(100% / $option-count);
aspect-ratio: 1;
border-radius: 50%;
background: var(--switch-theme-thumb-color);
pointer-events: none;
}

@for $i from 0 to $option-count {
.theme-#{$i}:checked ~ .switch-handle {
left: calc(#{$i} * $portion + ($portion - $handle-width));
}
}
</style>
</style>
It seems like the formula in the loop is wrong In addition, for performance reasons, I prefer using translate instead
No description
12 Replies
MarkBoots
MarkBoots9mo ago
I think it is because you didn't account for the flex-gap between the labels. It would be easier for us to help if you provide a live example.
avi12
avi129mo ago
Wdym a live example? A video?
MarkBoots
MarkBoots9mo ago
no, like in codepen (with normal html and SCSS) or something else
avi12
avi129mo ago
Oh I could try
MarkBoots
MarkBoots9mo ago
Maybe this is already enough your switch-handle should not be width: calc(100% / $option-count); but width: calc((100% - ($option-count - 1) * 0.62rem) / $option-count));
avi12
avi129mo ago
That worked, how'd you figure it out? Also, I do need to extent the circle padding
MarkBoots
MarkBoots9mo ago
bit hard to tell what you mean with that. live example still would be useful
avi12
avi129mo ago
I.e. the actual design looks like
No description
avi12
avi129mo ago
For some reason the SCSS doesn't get compiled https://codepen.io/avi12641/pen/mdaYmBP
avi12
avi129mo ago
I eventually figured it out:
$option-count: 3;
$padding: 6px;

.switch-handle {
position: absolute;
z-index: -1;
width: calc((100% - ($option-count - 1) * $gap) / $option-count + $padding);
aspect-ratio: 1;
border-radius: 50%;
background: var(--switch-theme-thumb-color);
pointer-events: none;
transition: left 0.4s ease-in-out;
}

@for $i from 0 to $option-count {
.theme-#{$i}:checked ~ .switch-handle {
left: calc(#{$i} * (100% / $option-count) - $padding / 2);
}
}
$option-count: 3;
$padding: 6px;

.switch-handle {
position: absolute;
z-index: -1;
width: calc((100% - ($option-count - 1) * $gap) / $option-count + $padding);
aspect-ratio: 1;
border-radius: 50%;
background: var(--switch-theme-thumb-color);
pointer-events: none;
transition: left 0.4s ease-in-out;
}

@for $i from 0 to $option-count {
.theme-#{$i}:checked ~ .switch-handle {
left: calc(#{$i} * (100% / $option-count) - $padding / 2);
}
}
@MarkBoots Thanks so much!
MarkBoots
MarkBoots9mo ago
ah okay. nice. didn't have time yet. But great you figured it out
avi12
avi129mo ago
Any idea how I could downsize it so that the starting percentage isn't 100%?
$option-count: 3;

.switch-handle {
position: absolute;
z-index: 0;
width: calc((80% - ($option-count - 1) * $gap) / $option-count);
aspect-ratio: 1;
border-radius: 50%;
background: var(--switch-theme-thumb-color);
pointer-events: none;
transition: left 0.4s ease-in-out;
}

@for $i from 0 to $option-count {
.theme-#{$i}:checked ~ .switch-handle {
left: calc(#{$i} * (100% / $option-count));
}
}
$option-count: 3;

.switch-handle {
position: absolute;
z-index: 0;
width: calc((80% - ($option-count - 1) * $gap) / $option-count);
aspect-ratio: 1;
border-radius: 50%;
background: var(--switch-theme-thumb-color);
pointer-events: none;
transition: left 0.4s ease-in-out;
}

@for $i from 0 to $option-count {
.theme-#{$i}:checked ~ .switch-handle {
left: calc(#{$i} * (100% / $option-count));
}
}
No description