braindrops.nicwands.com/shared/Btn.vue
2026-07-17 11:24:36 -04:00

148 lines
3.3 KiB
Vue

<template>
<component class="btn" :is="tag">
<span v-if="arrow" class="leading-icon"></span>
<span class="text">
<span><slot /></span>
<span><slot /></span>
</span>
<span v-if="arrow" class="arrow"></span>
</component>
</template>
<script setup>
const props = defineProps({
tag: {
type: String,
default: () => 'button',
},
arrow: {
type: Boolean,
default: () => false,
},
loading: {
type: Boolean,
default: () => false,
},
})
</script>
<style lang="scss">
.btn {
--t-duration: 350ms;
--t-ease: var(--ease-out-quad);
display: inline-flex;
align-items: center;
padding: rs(6px) rs(16px);
background: var(--theme-contrast);
color: var(--ivory);
border-radius: rs(8px);
position: relative;
overflow: hidden;
cursor: pointer;
transition: color var(--t-duration) var(--t-ease);
.leading-icon {
position: absolute;
width: rs(10px);
height: 1em;
display: flex;
align-items: center;
transform: translateX(-100%);
opacity: 0;
transition:
transform var(--t-duration) var(--t-ease),
opacity var(--t-duration) var(--t-ease);
}
.text {
position: relative;
display: block;
width: 100%;
overflow: hidden;
transition: transform var(--t-duration) var(--t-ease);
span {
display: inline-block;
transition: transform var(--t-duration) var(--t-ease);
@include label;
}
span:first-child {
color: var(--ivory);
}
span:last-child {
position: absolute;
left: 0;
right: 0;
bottom: 0;
transform: translateY(105%);
color: var(--ivory);
}
}
.arrow {
transition:
transform var(--t-duration) var(--t-ease),
opacity var(--t-duration) var(--t-ease);
&:not(:only-child) {
margin-left: 0.5em;
}
}
.loading {
width: rs(10px);
height: 1em;
display: flex;
align-items: center;
svg {
width: 100%;
height: auto;
}
}
&::before {
content: '';
display: block;
position: absolute;
inset: -2px;
background: var(--theme-contrast);
clip-path: inset(100% 0 0 0);
transition: clip-path var(--t-duration) var(--t-ease);
}
&:hover,
&.active,
&.router-link-active {
color: var(--ivory);
&::before {
clip-path: inset(0);
}
.text {
span:first-child {
transform: translateY(-105%);
}
span:last-child {
transform: none;
}
}
&.text-icon {
.leading-icon {
transform: none;
opacity: 1;
}
.text {
transform: translateX(1.5em);
}
.arrow {
transform: translateX(100%);
opacity: 0;
}
}
}
&:disabled,
&.disabled {
opacity: 0.16;
pointer-events: none;
}
}
</style>