Newer
Older
vmk-ui-kit / src / scss / components / _buttons.scss
/* =========================
   Buttons
   Figma: Buttons / Button — Large 64, Medium 48, Small 40,
          Xsmall 32, XXsmall 24.
========================= */

@use "sass:color";
@use "../kit-deps" as *;

@keyframes vmk-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

.btn {
  // Variant tokens
  --btn-bg: transparent;
  --btn-border: transparent;
  --btn-text: #{$neutral-900};
  --btn-hover-bg: var(--btn-bg);
  --btn-hover-border: var(--btn-border);
  --btn-hover-text: var(--btn-text);
  --btn-focus-bg: var(--btn-hover-bg);
  --btn-focus-border: var(--btn-hover-border);
  --btn-focus-text: var(--btn-hover-text);
  --btn-disabled-bg: var(--btn-bg);
  --btn-disabled-border: var(--btn-border);
  --btn-disabled-text: var(--btn-text);

  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: $space-2;
  font-family: $font-family-base;
  font-weight: $font-weight-semibold;
  line-height: 1;
  text-align: center;
  text-decoration: none;
  text-transform: none;
  white-space: nowrap;
  vertical-align: middle;
  border-width: $border-width-base;
  border-style: $border-style-base;
  border-color: var(--btn-border);
  border-radius: $radius-full;
  background-color: var(--btn-bg);
  color: var(--btn-text);
  cursor: pointer;
  transition:
    background-color $motion-base $motion-ease,
    border-color $motion-base $motion-ease,
    color $motion-base $motion-ease,
    box-shadow $motion-base $motion-ease;

  &:focus-visible {
    @include focus_ring;
  }

  &:hover:not(:disabled):not(.is-disabled):not(.is-loading),
  &.is-hover:not(:disabled):not(.is-disabled):not(.is-loading) {
    background-color: var(--btn-hover-bg);
    border-color: var(--btn-hover-border);
    color: var(--btn-hover-text);
  }

  &:active:not(:disabled):not(.is-disabled):not(.is-loading),
  &.is-active:not(:disabled):not(.is-disabled):not(.is-loading) {
    background-color: var(--btn-focus-bg);
    border-color: var(--btn-focus-border);
    color: var(--btn-focus-text);
  }

  &:disabled,
  &.is-disabled {
    background-color: var(--btn-disabled-bg) !important;
    border-color: var(--btn-disabled-border) !important;
    color: var(--btn-disabled-text) !important;
    cursor: not-allowed;
    opacity: 1;
  }

  &.is-loading {
    cursor: wait;

    .vmk-icon {
      animation: vmk-spin 1s linear infinite;
    }
  }

  .vmk-icon,
  .btn-arrow {
    color: currentColor;
    flex-shrink: 0;
  }

  .btn-arrow {
    width: 1em;
    height: 1em;
  }
}

// Sizes
// Reference API: sm (40), md (48), lg (64). Figma adds xs (32) and xxs (24).
.btn-lg {
  min-height: 64px;
  padding: $space-4 $space-6;
  font-size: 16px;
}

.btn-md {
  min-height: 48px;
  padding: $space-3 $space-5;
  font-size: 15px;
}

.btn-sm {
  min-height: 40px;
  padding: $space-2 $space-4;
  font-size: 13px;
}

.btn-xs {
  min-height: 32px;
  padding: $space-1 $space-3;
  font-size: 12px;
}

.btn-xxs {
  min-height: 24px;
  padding: $space-0 $space-2;
  font-size: 13px;
}

// Block button
.btn-block {
  display: flex;
  width: 100%;
}

// Icon-only: keep a perfect circle and equal padding.
.btn-icon-only {
  aspect-ratio: 1 / 1;
  padding: 0;

  &.btn-lg { width: 64px; }
  &.btn-md { width: 48px; }
  &.btn-sm { width: 40px; }
  &.btn-xs { width: 32px; }
  &.btn-xxs { width: 24px; }
}

.btn-icon {
  aspect-ratio: 1 / 1;
  padding: 0;
  border-radius: $radius-md;

  &.btn-icon-sm {
    width: 32px;
    height: 32px;
  }
}

// Variants aligned with Figma Button.png

// Primary — filled yellow CTA
.btn-primary {
  --btn-bg: #{$yellow-400};
  --btn-border: #{$yellow-400};
  --btn-text: #{$neutral-900};
  --btn-hover-bg: #{$yellow-500};
  --btn-hover-border: #{$yellow-500};
  --btn-focus-bg: #{$yellow-600};
  --btn-focus-border: #{$yellow-600};
  --btn-disabled-bg: #{$neutral-200};
  --btn-disabled-border: #{$neutral-200};
  --btn-disabled-text: #{$neutral-500};
}

// Accent — yellow alias, kept for API compatibility.
.btn-accent {
  --btn-bg: #{$yellow-400};
  --btn-border: #{$yellow-400};
  --btn-text: #{$neutral-900};
  --btn-hover-bg: #{$yellow-500};
  --btn-hover-border: #{$yellow-500};
  --btn-focus-bg: #{$yellow-600};
  --btn-focus-border: #{$yellow-600};
  --btn-disabled-bg: #{$neutral-200};
  --btn-disabled-border: #{$neutral-200};
  --btn-disabled-text: #{$neutral-500};
}

// Secondary — mint outline/fill (Figma secondary row)
.btn-secondary {
  --btn-bg: transparent;
  --btn-border: #{$mint-600};
  --btn-text: #{$mint-800};
  --btn-hover-bg: #{$mint-300};
  --btn-hover-border: #{$mint-600};
  --btn-hover-text: #{$mint-900};
  --btn-focus-bg: #{$mint-400};
  --btn-focus-border: #{$mint-600};
  --btn-focus-text: #{$mint-900};
  --btn-disabled-bg: transparent;
  --btn-disabled-border: #{$mint-200};
  --btn-disabled-text: #{$mint-400};
}

// Tertiary — ghost dark teal text (Figma tertiary row)
.btn-tertiary {
  --btn-bg: transparent;
  --btn-border: transparent;
  --btn-text: #{$mint-900};
  --btn-hover-bg: #{$neutral-100};
  --btn-hover-border: transparent;
  --btn-hover-text: #{$mint-900};
  --btn-focus-bg: #{$neutral-200};
  --btn-focus-border: transparent;
  --btn-focus-text: #{$mint-900};
  --btn-disabled-bg: transparent;
  --btn-disabled-border: transparent;
  --btn-disabled-text: #{$neutral-400};

  &.btn-icon-only {
    border-radius: $radius-md;
  }
}

// Semantic variants
.btn-success {
  --btn-bg: #{$success-bg};
  --btn-border: #{$success-text};
  --btn-text: #{$success-text};
  --btn-hover-bg: #{color.mix($success-text, $success-bg, 12%)};
  --btn-hover-border: #{$success-text};
  --btn-focus-bg: #{color.mix($success-text, $success-bg, 24%)};
  --btn-disabled-bg: #{$success-bg};
  --btn-disabled-border: #{rgba($success-text, 0.4)};
  --btn-disabled-text: #{rgba($success-text, 0.5)};
}

.btn-warning {
  --btn-bg: #{$warning-bg};
  --btn-border: #{$warning-text};
  --btn-text: #{$warning-text};
  --btn-hover-bg: #{color.mix($warning-text, $warning-bg, 12%)};
  --btn-hover-border: #{$warning-text};
  --btn-focus-bg: #{color.mix($warning-text, $warning-bg, 24%)};
  --btn-disabled-bg: #{$warning-bg};
  --btn-disabled-border: #{rgba($warning-text, 0.4)};
  --btn-disabled-text: #{rgba($warning-text, 0.5)};
}

.btn-info {
  --btn-bg: #{$info-bg};
  --btn-border: #{$info-text};
  --btn-text: #{$info-text};
  --btn-hover-bg: #{color.mix($info-text, $info-bg, 12%)};
  --btn-hover-border: #{$info-text};
  --btn-focus-bg: #{color.mix($info-text, $info-bg, 24%)};
  --btn-disabled-bg: #{$info-bg};
  --btn-disabled-border: #{rgba($info-text, 0.4)};
  --btn-disabled-text: #{rgba($info-text, 0.5)};
}

// Danger / Error — destructive red fill
.btn-danger,
.btn-error {
  --btn-bg: #{$danger-text};
  --btn-border: #{$danger-text};
  --btn-text: #{$neutral-0};
  --btn-hover-bg: #{color.adjust($danger-text, $lightness: -8%)};
  --btn-hover-border: #{color.adjust($danger-text, $lightness: -8%)};
  --btn-focus-bg: #{color.adjust($danger-text, $lightness: -12%)};
  --btn-focus-border: #{color.adjust($danger-text, $lightness: -12%)};
  --btn-disabled-bg: #{$danger-bg};
  --btn-disabled-border: #{$danger-bg};
  --btn-disabled-text: #{rgba($danger-text, 0.5)};
}

// Destructive secondary — light red outline
.btn-danger-secondary,
.btn-error-secondary {
  --btn-bg: #{$danger-bg};
  --btn-border: #{$danger-text};
  --btn-text: #{$danger-text};
  --btn-hover-bg: #{color.adjust($danger-bg, $lightness: -4%)};
  --btn-hover-border: #{$danger-text};
  --btn-focus-bg: #{color.adjust($danger-bg, $lightness: -8%)};
  --btn-focus-border: #{$danger-text};
  --btn-disabled-bg: #{$danger-bg};
  --btn-disabled-border: #{rgba($danger-text, 0.3)};
  --btn-disabled-text: #{rgba($danger-text, 0.5)};
}

// Destructive tertiary — red ghost
.btn-danger-tertiary,
.btn-error-tertiary {
  --btn-bg: transparent;
  --btn-border: transparent;
  --btn-text: #{$danger-text};
  --btn-hover-bg: #{$danger-bg};
  --btn-hover-border: transparent;
  --btn-focus-bg: #{color.adjust($danger-bg, $lightness: -4%)};
  --btn-focus-border: transparent;
  --btn-disabled-bg: transparent;
  --btn-disabled-border: transparent;
  --btn-disabled-text: #{rgba($danger-text, 0.5)};
}