Théo Lavaux
Théo Lavaux

Reputation: 1454

Vue dynamic styles binding is not fluid with computed properties

I have a NuxtJS app where I want create a custom <Radio> component.

I bind a CSS class dynamically so I can change the font-weight of my radio button once it's selected.

After the initial load and on the first click on the button, you can see that the text changes slightly in size, a kind of "shrinking/growing" effect but it doesn't happen again afterwards, the transition is more fluid afterwards. Could this be due to the way computed properties work?

I made a video of it to make it clearer.

Inspiration drawn from Stephanie Eckles

<template>
  <div :class="containerClasses">
    <label :class="radioClasses">
      <span class="radio__wrapper">
        <input
          :id="radioId"
          class="radio__input"
          type="radio"
          :checked="shouldBeChecked"
          :value="radioValue"
          @change="updateRadio"
        />
        <span class="radio__control"></span>
      </span>
      <span :for="radioId" class="radio__label">{{ radioLabel }}</span>
    </label>
  </div>
</template>

<script lang="ts">
import { defineComponent, toRefs, computed } from '@nuxtjs/composition-api';

export default defineComponent({
  name: 'Radio',
  model: {
    prop: 'modelValue',
    event: 'change',
  },
  props: {
    id: {
      type: String,
      required: true,
    },
    label: {
      type: String,
      required: true,
    },
    value: {
      type: String,
      default: undefined,
    },
    modelValue: {
      type: String,
      default: '',
    },
  },
  setup(props, { emit }) {
    const { label, id, value, modelValue } = toRefs(props);

    const updateRadio = () => {
      emit('change', value.value);
    };

    const shouldBeChecked = computed(() => modelValue.value === value.value);

    const containerClasses = computed(() => ({
      container: true,
      'container--active': shouldBeChecked.value,
    }));

    const radioClasses = computed(() => ({
      radio: true,
      'radio--active': shouldBeChecked.value,
    }));

    return {
      radioLabel: label,
      radioId: id,
      radioValue: value,
      shouldBeChecked,
      updateRadio,
      containerClasses,
      radioClasses,
    };
  },
});
</script>

<style lang="scss" scoped>
.container {
  --border-width: 1px;

  position: relative;
  border: solid var(--border-width) transparent;
  border-radius: 16px;
  background: $color-white;
  background-clip: padding-box;
  cursor: pointer;

  &::before {
    content: '';
    position: absolute;
    z-index: -1;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    margin: calc(var(--border-width) * -1);
    border-radius: inherit;
    background: $color-dark-grey;
  }

  &--active {
    &::before {
      background: $gradient-blue-purple;
    }
  }
}

.radio {
  display: grid;
  grid-gap: 1.6rem;
  grid-template-columns: min-content auto;
  padding: 2rem $spacing-s;
  font-size: $font-size-s;

  &__wrapper {
    display: flex;
  }

  &--active {
    font-weight: $font-weight-medium;
  }

  &__input {
    width: 0;
    height: 0;
    opacity: 0;

    + .radio__control::before {
      content: '';
      width: 1rem;
      height: 1rem;
      transform: scale(0);
      transition: 180ms transform ease-in-out;
      border-radius: 50%;
      box-shadow: inset 0.5em 0.5em currentColor;
    }

    &:checked + .radio__control::before {
      transform: scale(1);
    }
  }

  &__control {
    display: grid;
    width: 2.4rem;
    height: 2.4rem;
    transform: translateY(-0.1em);
    border: 1px solid currentColor;
    place-items: center;
    border-radius: 50%;
  }

  &__label {
    display: flex;
    align-items: center;
    line-height: 1;
  }
}
</style>

Upvotes: 1

Views: 217

Answers (1)

Th&#233;o Lavaux
Th&#233;o Lavaux

Reputation: 1454

This lead me to the right direction along with this style adjustement to make it look right.

// Before
.radio {
  display: grid;
  grid-gap: 1.6rem;
}

// After
.radio {
  display: flex;
  gap: 1.6rem;
}

Upvotes: 1

Related Questions