Files
xrpl-dev-portal/shared/components/Button/Button.scss
akcodez f20177b5f9 Add Tertiary Button Showcase Component
Introduced a new ButtonShowcaseTertiary component to demonstrate the BDS Tertiary Button, including its various states and usage examples. Removed the previous ButtonShowcase component to streamline the showcase and focus on the Tertiary variant. Updated Button component to support the new variant and adjusted related styles in the CSS for both green and black themes.
2025-12-01 14:07:29 -08:00

720 lines
24 KiB
SCSS

// BDS Button Component Styles
// Brand Design System - Scalable button component
//
// Naming Convention: BEM with 'bds' namespace
// .bds-btn - Base button (shared layout, typography, icon animation)
// .bds-btn--primary - Primary variant (solid background)
// .bds-btn--secondary - Secondary variant (outline style)
// .bds-btn--green - Green color theme (default)
// .bds-btn--black - Black color theme
// .bds-btn--tertiary - Tertiary variant (text-only style)
// .bds-btn__label - Label element
// .bds-btn__icon - Icon element (inherits color via currentColor)
// .bds-btn--disabled - Disabled state modifier
// .bds-btn--no-icon - No icon modifier
@import "../../../styles/colors";
// =============================================================================
// Design Tokens
// =============================================================================
// Neutral Black (used across variants)
$bds-btn-neutral-black: #141414;
$bds-btn-neutral-black-80: rgba(20, 20, 20, 0.8); // 80% black for hover
$bds-btn-neutral-black-15: rgba(20, 20, 20, 0.15); // 15% black for secondary hover
// Colors - Green Primary Button
$bds-btn-primary-bg: $green-300; // #21E46B - Enabled
$bds-btn-primary-bg-hover: $green-200; // #70EE97 - Hover/Focus
$bds-btn-primary-text: $bds-btn-neutral-black;
$bds-btn-primary-focus-border: $bds-btn-neutral-black;
// Colors - Black Primary Button
$bds-btn-primary-black-bg: $bds-btn-neutral-black;
$bds-btn-primary-black-bg-hover: $bds-btn-neutral-black-80;
$bds-btn-primary-black-text: $white;
// Colors - Disabled State (shared)
$bds-btn-disabled-bg: $gray-200; // #E0E0E1
$bds-btn-disabled-text: $gray-500; // #838386
// Colors - Green Secondary Button
$bds-btn-secondary-text: $green-400; // #0DAA3E - Enabled
$bds-btn-secondary-text-hover: $green-500; // #078139 - Hover/Focus
$bds-btn-secondary-bg: transparent;
$bds-btn-secondary-bg-hover: $green-100; // #EAFCF1 - Hover/Focus fill
$bds-btn-secondary-border: $green-400; // #0DAA3E - Enabled
$bds-btn-secondary-border-hover: $green-500; // #078139 - Hover/Focus
$bds-btn-secondary-disabled-text: $gray-400; // Disabled text
$bds-btn-secondary-disabled-border: $gray-400; // Disabled border
// Colors - Tertiary Button
$bds-btn-tertiary-text: $green-400; // #0DAA3E - Enabled
$bds-btn-tertiary-text-hover: $green-500; // #078139 - Hover/Focus/Active
$bds-btn-tertiary-bg: transparent;
$bds-btn-tertiary-focus-outline: $bds-btn-neutral-black; // #141414 - Focus outline (black)
$bds-btn-tertiary-disabled-text: $gray-400; // Disabled text
// Colors - Black Secondary Button
$bds-btn-secondary-black-text: $bds-btn-neutral-black;
$bds-btn-secondary-black-bg-hover: $bds-btn-neutral-black-15;
$bds-btn-secondary-black-border: $bds-btn-neutral-black;
// Colors - Black Tertiary Button
$bds-btn-tertiary-black-text: $bds-btn-neutral-black;
$bds-btn-tertiary-black-focus-outline: $bds-btn-neutral-black;
// Spacing
$bds-btn-border-radius: 100px;
$bds-btn-focus-border-width: 2px;
// Transitions
// Motion: Duration 150ms, Custom bezier for smooth in-out feel
$bds-btn-transition-duration: 150ms;
$bds-btn-transition-timing: cubic-bezier(0.98, 0.12, 0.12, 0.98);
// =============================================================================
// Base Button Styles
// =============================================================================
//
// SHARED HOVER ANIMATION:
// - Background fills from bottom-to-top using ::before pseudo-element
// - On unhover: background empties top-to-bottom
// - Each variant defines its own ::before background-color
.bds-btn {
// Layout
display: inline-flex;
align-items: center;
justify-content: center;
max-height: 40px;
// Typography - Label R token
font-family: 'Booton', sans-serif;
font-size: 16px;
font-weight: 400;
line-height: 23.2px;
letter-spacing: 0px;
white-space: nowrap;
// Border
border: none;
border-radius: $bds-btn-border-radius;
// Interaction
cursor: pointer;
// Required for pseudo-element background animation (shared)
position: relative;
overflow: hidden;
z-index: 1;
// Transitions (shared)
transition: color $bds-btn-transition-duration $bds-btn-transition-timing,
border-color $bds-btn-transition-duration $bds-btn-transition-timing,
padding $bds-btn-transition-duration $bds-btn-transition-timing,
gap $bds-btn-transition-duration $bds-btn-transition-timing,
transform $bds-btn-transition-duration $bds-btn-transition-timing;
// Background fill pseudo-element (shared structure)
// Each variant sets its own background-color
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: -1;
// Start scaled to 0 from bottom - fills bottom-to-top on hover
transform: scaleY(0);
transform-origin: bottom center;
transition: transform $bds-btn-transition-duration $bds-btn-transition-timing;
}
// Hover/Focus state - animate background fill (shared)
&:hover:not(:disabled):not(.bds-btn--disabled),
&:focus-visible:not(:disabled):not(.bds-btn--disabled) {
&::before {
transform: scaleY(1);
}
}
// Active state - reset background (shared)
&:active:not(:disabled):not(.bds-btn--disabled) {
&::before {
transform: scaleY(0);
}
}
// Label element
&__label {
flex-shrink: 0;
position: relative;
z-index: 1;
}
// Icon element (SVG container)
&__icon {
width: 15px;
height: 14px;
flex-shrink: 0;
transition: opacity $bds-btn-transition-duration $bds-btn-transition-timing;
color: currentColor;
overflow: visible;
position: relative;
z-index: 1;
}
// Arrow horizontal line - shrinks on hover/focus
&__icon-line {
transform-box: fill-box; // Makes transform-origin relative to element's bounding box
transform-origin: right center;
transform: scaleX(1);
transition: transform $bds-btn-transition-duration $bds-btn-transition-timing;
}
// Arrow chevron - stays visible, shifts via gap change
&__icon-chevron {
transition: transform $bds-btn-transition-duration $bds-btn-transition-timing;
}
// Hover state - shrink line for all button variants
&:hover:not(:disabled):not(.bds-btn--disabled),
&:focus-visible:not(:disabled):not(.bds-btn--disabled) {
.bds-btn__icon-line {
transform: scaleX(0);
}
}
}
// =============================================================================
// Primary Variant
// =============================================================================
// HOVER ANIMATION: Background fills bottom-to-top (Green 300 → Green 200)
.bds-btn--primary {
// Default/Enabled state colors
color: $bds-btn-primary-text;
background-color: $bds-btn-primary-bg;
// Set the hover background color for ::before pseudo-element
&::before {
background-color: $bds-btn-primary-bg-hover;
}
// Desktop padding and gap (default - ≥1024px)
padding: 8px 19px 8px 20px;
gap: 16px;
// No icon - symmetric padding
&.bds-btn--no-icon {
padding: 8px 20px;
}
// ---------------------------------------------------------------------------
// Hover State (with icon)
// ---------------------------------------------------------------------------
&:hover:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) {
// Background animation handled by shared ::before pseudo-element
padding: 8px 13px 8px 20px;
gap: 22px;
}
// Hover State (no icon) - background animation only
// (no padding/gap changes needed)
// ---------------------------------------------------------------------------
// Focus State (keyboard navigation) - with icon
// ---------------------------------------------------------------------------
&:focus-visible:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) {
outline: $bds-btn-focus-border-width solid $bds-btn-primary-focus-border;
outline-offset: 2px;
padding: 8px 13px 8px 20px;
gap: 22px;
}
// Focus State (no icon) - only add outline
&:focus-visible:not(:disabled):not(.bds-btn--disabled).bds-btn--no-icon {
outline: $bds-btn-focus-border-width solid $bds-btn-primary-focus-border;
outline-offset: 2px;
padding: 8px 20px;
}
// ---------------------------------------------------------------------------
// Active State (being pressed)
// ---------------------------------------------------------------------------
&:active:not(:disabled):not(.bds-btn--disabled) {
// Maintains default padding and gap
padding: 8px 19px 8px 20px;
gap: 16px;
// Background reset handled by shared ::before in base class
}
// ---------------------------------------------------------------------------
// Disabled State
// Note: Icon is hidden via component logic when disabled
// ---------------------------------------------------------------------------
&:disabled,
&.bds-btn--disabled {
color: $bds-btn-disabled-text;
background-color: $bds-btn-disabled-bg;
cursor: not-allowed;
pointer-events: none;
// Disable background animation on disabled
&::before {
display: none;
}
}
// ---------------------------------------------------------------------------
// Tablet & Mobile Responsive Styles (≤1023px)
// ---------------------------------------------------------------------------
@media (max-width: 1023px) {
// Default/Enabled padding for smaller screens
padding: 8px 15px 8px 16px;
gap: 16px;
// No icon - symmetric padding for smaller screens
&.bds-btn--no-icon {
padding: 8px 16px;
}
// Hover state - smaller screens (with icon)
&:hover:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) {
padding: 8px 10px 8px 16px;
gap: 21px;
}
// Focus state - smaller screens (with icon)
&:focus-visible:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) {
outline: $bds-btn-focus-border-width solid $bds-btn-primary-focus-border;
outline-offset: 2px;
padding: 8px 10px 8px 16px;
gap: 21px;
}
// Focus state - smaller screens (no icon)
&:focus-visible:not(:disabled):not(.bds-btn--disabled).bds-btn--no-icon {
outline: $bds-btn-focus-border-width solid $bds-btn-primary-focus-border;
outline-offset: 2px;
padding: 8px 16px;
}
// Active state - smaller screens (maintains default padding)
&:active:not(:disabled):not(.bds-btn--disabled) {
padding: 8px 15px 8px 16px;
gap: 16px;
}
}
}
// =============================================================================
// Secondary Variant
// =============================================================================
// HOVER ANIMATION: Background fills bottom-to-top (Transparent → Green 100)
// Text/arrow color fades from green-400 to green-500 via currentColor
// Animation structure inherited from base .bds-btn class
.bds-btn--secondary {
// Default/Enabled state
color: $bds-btn-secondary-text;
background-color: transparent;
border: 2px solid $bds-btn-secondary-border;
// Set the hover background color for ::before pseudo-element
&::before {
background-color: $bds-btn-secondary-bg-hover;
}
// Desktop padding and gap (≥1024px) - compensate for 2px border
padding: 6px 17px 6px 18px;
gap: 16px;
// No icon - symmetric padding
&.bds-btn--no-icon {
padding: 6px 18px;
}
// ---------------------------------------------------------------------------
// Hover State
// ---------------------------------------------------------------------------
&:hover:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) {
color: $bds-btn-secondary-text-hover;
border-color: $bds-btn-secondary-border-hover;
padding: 6px 11px 6px 18px;
gap: 22px;
// Background animation handled by shared ::before in base class
}
&:hover:not(:disabled):not(.bds-btn--disabled).bds-btn--no-icon {
color: $bds-btn-secondary-text-hover;
border-color: $bds-btn-secondary-border-hover;
}
// ---------------------------------------------------------------------------
// Focus State (keyboard navigation)
// ---------------------------------------------------------------------------
&:focus-visible:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) {
color: $bds-btn-secondary-text-hover;
border: none;
outline: $bds-btn-focus-border-width solid $bds-btn-primary-focus-border;
outline-offset: 2px;
// Add 2px padding to compensate for removed border, maintain hover padding
padding: 6px 13px 6px 20px;
gap: 22px;
}
&:focus-visible:not(:disabled):not(.bds-btn--disabled).bds-btn--no-icon {
color: $bds-btn-secondary-text-hover;
border: none;
outline: $bds-btn-focus-border-width solid $bds-btn-primary-focus-border;
outline-offset: 2px;
// Add 2px padding to compensate for removed border
padding: 6px 20px;
}
// ---------------------------------------------------------------------------
// Active State (being pressed)
// ---------------------------------------------------------------------------
&:active:not(:disabled):not(.bds-btn--disabled) {
color: $bds-btn-secondary-text;
border-color: $bds-btn-secondary-border;
padding: 6px 17px 6px 18px;
gap: 16px;
// Background reset handled by shared ::before in base class
}
// ---------------------------------------------------------------------------
// Disabled State
// Note: Icon is hidden via component logic when disabled
// ---------------------------------------------------------------------------
&:disabled,
&.bds-btn--disabled {
color: $bds-btn-secondary-disabled-text;
background-color: transparent;
border-color: $bds-btn-secondary-disabled-border;
cursor: not-allowed;
pointer-events: none;
// Disable background animation on disabled
&::before {
display: none;
}
}
// ---------------------------------------------------------------------------
// Tablet & Mobile Responsive Styles (≤1023px)
// ---------------------------------------------------------------------------
@media (max-width: 1023px) {
padding: 6px 13px 6px 14px;
gap: 16px;
&.bds-btn--no-icon {
padding: 6px 14px;
}
&:hover:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) {
padding: 6px 8px 6px 14px;
gap: 21px;
}
&:focus-visible:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) {
border: none;
outline: $bds-btn-focus-border-width solid $bds-btn-primary-focus-border;
outline-offset: 2px;
// Add 2px padding to compensate for removed border, maintain hover padding
padding: 6px 10px 6px 16px;
gap: 21px;
}
&:focus-visible:not(:disabled):not(.bds-btn--disabled).bds-btn--no-icon {
border: none;
outline: $bds-btn-focus-border-width solid $bds-btn-primary-focus-border;
outline-offset: 2px;
// Add 2px padding to compensate for removed border
padding: 6px 16px;
}
&:active:not(:disabled):not(.bds-btn--disabled) {
padding: 6px 13px 6px 14px;
gap: 16px;
}
}
}
// =============================================================================
// Color Themes
// =============================================================================
// Green theme (.bds-btn--green) is the default - no overrides needed.
// Black theme (.bds-btn--black) overrides colors for all variants.
// Black theme - overrides colors for primary, secondary, and tertiary variants
.bds-btn--black {
// ---------------------------------------------------------------------------
// Black Primary Button
// ---------------------------------------------------------------------------
&.bds-btn--primary {
color: $bds-btn-primary-black-text;
background-color: $bds-btn-primary-black-bg;
// Set hover background for ::before pseudo-element
&::before {
background-color: $bds-btn-primary-black-bg-hover;
}
// Hover state - background animates, colors stay same
&:hover:not(:disabled):not(.bds-btn--disabled) {
color: $bds-btn-primary-black-text;
}
// Focus state
&:focus-visible:not(:disabled):not(.bds-btn--disabled) {
color: $bds-btn-primary-black-text;
outline: $bds-btn-focus-border-width solid $bds-btn-neutral-black;
outline-offset: 2px;
}
// Active state
&:active:not(:disabled):not(.bds-btn--disabled) {
color: $bds-btn-primary-black-text;
}
// Disabled state - same as green disabled
&:disabled,
&.bds-btn--disabled {
color: $bds-btn-disabled-text;
background-color: $bds-btn-disabled-bg;
}
}
// ---------------------------------------------------------------------------
// Black Secondary Button
// ---------------------------------------------------------------------------
&.bds-btn--secondary {
color: $bds-btn-secondary-black-text;
border-color: $bds-btn-secondary-black-border;
// Set hover background for ::before pseudo-element
&::before {
background-color: $bds-btn-secondary-black-bg-hover;
}
// Hover state - text stays black, background fills with 15% black
&:hover:not(:disabled):not(.bds-btn--disabled) {
color: $bds-btn-secondary-black-text;
border-color: $bds-btn-secondary-black-border;
}
// Focus state
&:focus-visible:not(:disabled):not(.bds-btn--disabled) {
color: $bds-btn-secondary-black-text;
border: none;
outline: $bds-btn-focus-border-width solid $bds-btn-neutral-black;
outline-offset: 2px;
}
// Active state
&:active:not(:disabled):not(.bds-btn--disabled) {
color: $bds-btn-secondary-black-text;
border-color: $bds-btn-secondary-black-border;
}
// Disabled state
&:disabled,
&.bds-btn--disabled {
color: $bds-btn-disabled-text;
border-color: $bds-btn-secondary-disabled-border;
}
}
// ---------------------------------------------------------------------------
// Black Tertiary Button
// ---------------------------------------------------------------------------
&.bds-btn--tertiary {
color: $bds-btn-tertiary-black-text;
// Hover state - text stays black, underline appears (override base tertiary green hover)
&:hover:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) {
color: $bds-btn-tertiary-black-text !important; // Ensure black color overrides green
text-decoration: underline;
padding: 8px 14px 8px 20px; // Match base tertiary hover padding
gap: 22px; // Match base tertiary hover gap
}
&:hover:not(:disabled):not(.bds-btn--disabled).bds-btn--no-icon {
color: $bds-btn-tertiary-black-text !important; // Ensure black color overrides green
text-decoration: underline;
}
// Focus state
&:focus-visible:not(:disabled):not(.bds-btn--disabled) {
color: $bds-btn-tertiary-black-text !important; // Ensure black color overrides green
text-decoration: underline;
border-radius: 0; // Square corners for focus outline
outline: 2px solid $bds-btn-tertiary-black-focus-outline;
outline-offset: -2px; // Negative offset brings outline closer to content
// Padding will be inherited from base tertiary focus state
}
// Active state
&:active:not(:disabled):not(.bds-btn--disabled) {
color: $bds-btn-tertiary-black-text !important; // Ensure black color overrides green
text-decoration: underline;
}
// Disabled state - same as green disabled
&:disabled,
&.bds-btn--disabled {
color: $bds-btn-disabled-text;
}
// Responsive hover states - ensure black color on mobile too
@media (max-width: 1023px) {
&:hover:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) {
color: $bds-btn-tertiary-black-text !important;
padding: 8px 11px 8px 16px; // Match base tertiary mobile hover padding
gap: 21px;
}
}
}
}
// =============================================================================
// Tertiary Variant
// =============================================================================
// NOTE: Arrow icon animation is inherited from base .bds-btn class.
// The icon color automatically follows text color via currentColor.
// Only define: colors, text decoration, padding, gap, and focus outline.
.bds-btn--tertiary {
// Default/Enabled state
color: $bds-btn-tertiary-text;
background-color: $bds-btn-tertiary-bg;
border: none;
text-decoration: none;
// Typography - Body R token (different from Primary/Secondary)
font-size: 18px;
line-height: 26.1px;
letter-spacing: -0.5px;
// Desktop padding and gap (≥1024px)
padding: 8px 20px;
gap: 16px;
// No icon - symmetric padding
&.bds-btn--no-icon {
padding: 8px 20px;
}
// Disable background animation (no ::before needed for tertiary)
&::before {
display: none;
}
// ---------------------------------------------------------------------------
// Hover State
// ---------------------------------------------------------------------------
&:hover:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) {
color: $bds-btn-tertiary-text-hover;
text-decoration: underline;
padding: 8px 14px 8px 20px;
gap: 22px;
}
&:hover:not(:disabled):not(.bds-btn--disabled).bds-btn--no-icon {
color: $bds-btn-tertiary-text-hover;
text-decoration: underline;
}
// ---------------------------------------------------------------------------
// Focus State (keyboard navigation)
// ---------------------------------------------------------------------------
&:focus-visible:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) {
color: $bds-btn-tertiary-text-hover;
text-decoration: underline;
border-radius: 0; // Square corners for focus outline
outline: 2px solid $bds-btn-tertiary-focus-outline;
outline-offset: -2px; // Negative offset brings outline closer to content
padding: 0 8px 0 9px; // Reduced left padding by 5px (was 14px)
gap: 22px;
}
&:focus-visible:not(:disabled):not(.bds-btn--disabled).bds-btn--no-icon {
color: $bds-btn-tertiary-text-hover;
text-decoration: underline;
border-radius: 0; // Square corners for focus outline
outline: 2px solid $bds-btn-tertiary-focus-outline;
outline-offset: -2px; // Negative offset brings outline closer to content
padding: 0 9px; // Reduced left padding by 5px (was 14px)
}
// ---------------------------------------------------------------------------
// Active State (being pressed)
// ---------------------------------------------------------------------------
&:active:not(:disabled):not(.bds-btn--disabled) {
color: $bds-btn-tertiary-text;
text-decoration: underline;
padding: 8px 20px;
gap: 16px;
}
// ---------------------------------------------------------------------------
// Disabled State
// Note: Icon is hidden via component logic when disabled
// ---------------------------------------------------------------------------
&:disabled,
&.bds-btn--disabled {
color: $bds-btn-tertiary-disabled-text;
background-color: transparent;
text-decoration: none;
cursor: not-allowed;
pointer-events: none;
// Disable background animation on disabled
&::before {
display: none;
}
}
// ---------------------------------------------------------------------------
// Tablet & Mobile Responsive Styles (≤1023px)
// ---------------------------------------------------------------------------
@media (max-width: 1023px) {
padding: 8px 16px;
gap: 16px;
&.bds-btn--no-icon {
padding: 8px 16px;
}
&:hover:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) {
padding: 8px 11px 8px 16px;
gap: 21px;
}
&:focus-visible:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) {
border-radius: 0; // Square corners for focus outline
outline: 2px solid $bds-btn-tertiary-focus-outline;
outline-offset: -2px; // Negative offset brings outline closer to content
padding: 0 5px 0 2px; // Reduced left padding by 5px (was 10px)
gap: 21px;
}
&:focus-visible:not(:disabled):not(.bds-btn--disabled).bds-btn--no-icon {
border-radius: 0; // Square corners for focus outline
outline: 2px solid $bds-btn-tertiary-focus-outline;
outline-offset: -2px; // Negative offset brings outline closer to content
padding: 0 2px; // Reduced left padding by 5px (was 10px)
}
&:active:not(:disabled):not(.bds-btn--disabled) {
padding: 8px 16px;
gap: 16px;
}
}
}