diff --git a/about/button-showcase-primary.page.tsx b/about/button-showcase-primary.page.tsx new file mode 100644 index 0000000000..f963ca0b74 --- /dev/null +++ b/about/button-showcase-primary.page.tsx @@ -0,0 +1,456 @@ +import * as React from 'react'; +import { Button } from 'shared/components/Button'; +import { PageGrid, PageGridCol, PageGridRow } from 'shared/components/PageGrid/page-grid'; + +export const frontmatter = { + seo: { + title: 'BDS Button Component Showcase', + description: 'Interactive showcase of the Brand Design System Button component with all states and variants.', + }, +}; + +export default function ButtonShowcase() { + const [clickCount, setClickCount] = React.useState(0); + + const handleClick = () => { + setClickCount((prev) => prev + 1); + }; + + return ( +
+
+
+

BDS Button Component

+
Brand Design System
+
+

+ A scalable button component following the XRPL Brand Design System. This showcase demonstrates all states, + responsive behavior, and accessibility features of the Primary button variant. +

+
+ + {/* Basic Usage */} +
+
+

Basic Usage

+
Primary Variant
+
+
+ + + +
+ {clickCount > 0 && ( +

Button clicked {clickCount} time{clickCount !== 1 ? 's' : ''}

+ )} +
+ + {/* States */} +
+
+

Button States

+
Interactive States
+
+ + + +
+
Enabled State
+

Default state when button is ready for interaction.

+ +
+
+ +
+
Disabled State
+

Button cannot be interacted with.

+ +
+
+
+
+
+
Hover & Focus States
+

+ Hover over the buttons below or use Tab to focus them. Notice the background color change and icon swap. +

+
+ + +
+
+
+ + {/* Black Color Variant */} +
+
+

Black Color Variant

+
Color Theme
+
+

+ Primary buttons can use a black color theme for dark backgrounds or alternative styling needs. +

+
+ + + +
+
+ + {/* Black Variant States */} +
+
+

Black Variant States

+
Interactive States
+
+ + + +
+
Enabled State
+

Black background with white text.

+ +
+
+ +
+
Disabled State
+

Same disabled styling as green variant.

+ +
+
+
+
+
+
Hover & Focus States
+

+ Hover over the buttons or use Tab to focus them. Notice the background darkens slightly on hover. +

+
+ + +
+
+
+ + {/* Green vs Black Comparison */} +
+
+

Green vs Black Comparison

+
Color Themes
+
+

Compare the green (default) and black color themes side by side.

+
+ + +
+
+ + {/* Without Icon */} +
+
+

Without Icon

+
Icon Control
+
+

Buttons can be rendered without the arrow icon when needed.

+
+ + +
+
+ + {/* Button Types */} +
+
+

Button Types

+
Form Integration
+
+

Different button types for form submission and actions.

+
{ + e.preventDefault(); + alert('Form submitted!'); + }} + className="d-flex flex-wrap" + > + + + +
+
+ + {/* Responsive Behavior */} +
+
+

Responsive Behavior

+
Breakpoint Adjustments
+
+

+ Button padding adjusts automatically across breakpoints. Resize your browser window to see the changes: +

+ +
+ + +
+
+ + {/* Accessibility */} +
+
+

Accessibility Features

+
WCAG Compliance
+
+ + + +
Keyboard Navigation
+
    +
  • Tab to focus buttons
  • +
  • Enter or Space to activate
  • +
  • Focus indicator: 2px black border
  • +
  • Disabled buttons are not focusable
  • +
+
+ +
Screen Reader Support
+
    +
  • Button labels are announced
  • +
  • Disabled state communicated via aria-disabled
  • +
  • Icons are hidden from screen readers (aria-hidden)
  • +
  • Semantic button element used
  • +
+
+
+
+
+
Color Contrast
+
    +
  • + Enabled: Black text (#141414) on Green 300 (#21E46B) = 9.06:1 (AAA) +
  • +
  • + Hover: Black text (#141414) on Green 200 (#70EE97) = 10.23:1 (AAA) +
  • +
  • + Disabled: Gray 500 (#838386) on Gray 200 (#E0E0E1) = 2.12:1 (acceptable for disabled + state) +
  • +
+
+
+ + {/* Code Examples */} +
+
+

Code Examples

+
Implementation
+
+
+
+            {`import { Button } from 'shared/components/Button';
+
+// Basic usage (green theme - default)
+
+
+// Black color theme
+
+
+// Disabled state
+
+
+// Without icon
+
+
+// Form integration
+`}
+          
+
+
+ + {/* Design Specifications */} +
+
+

Design Specifications

+
Visual Details
+
+ + + +
Typography
+
    +
  • Font: Booton, sans-serif
  • +
  • Size: 16px
  • +
  • Weight: 400
  • +
  • Line Height: 23.2px
  • +
  • Letter Spacing: 0px
  • +
+
+ +
Spacing & Layout
+
    +
  • Border Radius: 100px (fully rounded)
  • +
  • Icon Size: 15px × 14px
  • +
  • Icon Gap: 16px (default), 22px (hover/focus desktop), 21px (hover/focus mobile)
  • +
  • Min Height: 40px (touch target)
  • +
+
+
+
+
+
State Colors - Green Theme
+
+ {/* Header */} +
+
State
+
Text Color
+
Background Color
+
Border
+
+ {/* Rows */} +
+
Enabled
+
#141414 (Neutral Black)
+
#21E46B (Green 300)
+
None
+
+
+
Hover
+
#141414 (Neutral Black)
+
#70EE97 (Green 200)
+
None
+
+
+
Focus
+
#141414 (Neutral Black)
+
#70EE97 (Green 200)
+
2px solid #141414
+
+
+
Active
+
#141414 (Neutral Black)
+
#21E46B (Green 300)
+
None
+
+
+
Disabled
+
#838386 (Gray 500)
+
#E0E0E1 (Gray 200)
+
None
+
+
+
+
+
State Colors - Black Theme
+
+ {/* Header */} +
+
State
+
Text Color
+
Background Color
+
Border
+
+ {/* Rows */} +
+
Enabled
+
#FFFFFF (White)
+
#141414 (Neutral Black)
+
None
+
+
+
Hover
+
#FFFFFF (White)
+
rgba(20, 20, 20, 0.8) (80% Black)
+
None
+
+
+
Focus
+
#FFFFFF (White)
+
rgba(20, 20, 20, 0.8) (80% Black)
+
2px solid #141414
+
+
+
Active
+
#FFFFFF (White)
+
#141414 (Neutral Black)
+
None
+
+
+
Disabled
+
#838386 (Gray 500)
+
#E0E0E1 (Gray 200)
+
None
+
+
+
+
+
+ ); +} diff --git a/about/button-showcase-secondary.page.tsx b/about/button-showcase-secondary.page.tsx new file mode 100644 index 0000000000..ce3ffc02df --- /dev/null +++ b/about/button-showcase-secondary.page.tsx @@ -0,0 +1,528 @@ +import * as React from 'react'; +import { Button } from 'shared/components/Button'; +import { PageGrid, PageGridCol, PageGridRow } from 'shared/components/PageGrid/page-grid'; + +export const frontmatter = { + seo: { + title: 'BDS Secondary Button Component Showcase', + description: 'Interactive showcase of the Brand Design System Secondary Button component with all states and variants.', + }, +}; + +export default function ButtonShowcaseSecondary() { + const [clickCount, setClickCount] = React.useState(0); + + const handleClick = () => { + setClickCount((prev) => prev + 1); + }; + + return ( +
+
+
+

BDS Secondary Button

+
Brand Design System
+
+

+ The Secondary button is an outline-style button used for secondary actions. It features a transparent + background with a green stroke/border, providing visual hierarchy below the Primary button while maintaining + brand consistency. +

+
+ + {/* Basic Usage */} +
+
+

Basic Usage

+
Secondary Variant
+
+
+ + + +
+ {clickCount > 0 && ( +

+ Button clicked {clickCount} time{clickCount !== 1 ? 's' : ''} +

+ )} +
+ + {/* Primary vs Secondary Comparison */} +
+
+

Primary vs Secondary

+
Visual Hierarchy
+
+

+ Use Primary for main actions and Secondary for supporting actions to create clear visual hierarchy. +

+
+ + +
+
+ + {/* States */} +
+
+

Button States

+
Interactive States
+
+ + + +
+
Enabled State
+

Default outline style with green border and text.

+ +
+
+ +
+
Disabled State
+

Gray border and text indicate non-interactive state.

+ +
+
+
+
+
+
Hover & Focus States
+

+ Hover over the buttons or use Tab to focus them. Notice the light green background fill and darker green + border on hover/focus. +

+
+ + +
+
+
+ + {/* Black Color Variant */} +
+
+

Black Color Variant

+
Color Theme
+
+

+ Secondary buttons can use a black color theme with black text and border instead of green. +

+
+ + + +
+
+ + {/* Black Variant States */} +
+
+

Black Variant States

+
Interactive States
+
+ + + +
+
Enabled State
+

Black border and text with transparent background.

+ +
+
+ +
+
Disabled State
+

Same disabled styling as green variant.

+ +
+
+
+
+
+
Hover & Focus States
+

+ Hover over the buttons or use Tab to focus them. Notice the subtle black background fill on hover. +

+
+ + +
+
+
+ + {/* Green vs Black Comparison */} +
+
+

Green vs Black Comparison

+
Color Themes
+
+

Compare the green (default) and black color themes side by side.

+
+ + +
+
+ + {/* Without Icon */} +
+
+

Without Icon

+
Icon Control
+
+

Secondary buttons can also be rendered without the arrow icon.

+
+ + +
+
+ + {/* Button Types */} +
+
+

Button Types

+
Form Integration
+
+

Secondary buttons can be used for form actions like cancel or reset.

+
{ + e.preventDefault(); + alert('Form submitted!'); + }} + className="d-flex flex-wrap" + > + + + +
+
+ + {/* Responsive Behavior */} +
+
+

Responsive Behavior

+
Breakpoint Adjustments
+
+

+ Button padding adjusts automatically across breakpoints. Resize your browser window to see the changes: +

+ +
+ + +
+
+ + {/* Accessibility */} +
+
+

Accessibility Features

+
WCAG Compliance
+
+ + + +
Keyboard Navigation
+
    +
  • Tab to focus buttons
  • +
  • Enter or Space to activate
  • +
  • Focus indicator: 2px black outline (additional to green border)
  • +
  • Disabled buttons are not focusable
  • +
+
+ +
Screen Reader Support
+
    +
  • Button labels are announced
  • +
  • Disabled state communicated via aria-disabled
  • +
  • Icons are hidden from screen readers (aria-hidden)
  • +
  • Semantic button element used
  • +
+
+
+
+
+
Color Contrast
+
    +
  • + Enabled: Green 400 (#0DAA3E) on White = 4.52:1 (AA for large text) +
  • +
  • + Hover: Green 500 (#078139) on Green 100 (#EAFCF1) = 4.87:1 (AA) +
  • +
  • + Disabled: Gray 400 (#A2A2A4) on White = reduced contrast (acceptable for disabled state) +
  • +
+
+
+ + {/* Code Examples */} +
+
+

Code Examples

+
Implementation
+
+
+
+            {`import { Button } from 'shared/components/Button';
+
+// Basic secondary button (green theme - default)
+
+
+// Black color theme
+
+
+// Disabled state
+
+
+// Without icon
+
+
+// Primary + Secondary pairing
+
+`}
+          
+
+
+ + {/* Design Specifications */} +
+
+

Design Specifications

+
Visual Details
+
+ + + +
Typography
+
    +
  • Font: Booton, sans-serif
  • +
  • Size: 16px
  • +
  • Weight: 400
  • +
  • Line Height: 23.2px
  • +
  • Letter Spacing: 0px
  • +
+
+ +
Spacing & Layout
+
    +
  • Border Radius: 100px (fully rounded)
  • +
  • Border Width: 2px solid
  • +
  • Icon Size: 15px × 14px
  • +
  • Icon Gap: 16px (default), 22px (hover/focus desktop), 21px (hover/focus mobile)
  • +
  • Max Height: 40px
  • +
+
+
+
+
+
State Colors - Green Theme
+
+ {/* Header */} +
+
State
+
Text Color
+
Background
+
Border
+
+ {/* Rows */} +
+
Enabled
+
#0DAA3E (Green 400)
+
Transparent
+
2px #0DAA3E (Green 400)
+
+
+
Hover
+
#078139 (Green 500)
+
#EAFCF1 (Green 100)
+
2px #078139 (Green 500)
+
+
+
Focus
+
#078139 (Green 500)
+
#EAFCF1 (Green 100)
+
2px #078139 + 2px #141414 outline
+
+
+
Active
+
#0DAA3E (Green 400)
+
Transparent
+
2px #0DAA3E (Green 400)
+
+
+
Disabled
+
#A2A2A4 (Gray 400)
+
Transparent
+
2px #A2A2A4 (Gray 400)
+
+
+
+
+
State Colors - Black Theme
+
+ {/* Header */} +
+
State
+
Text Color
+
Background
+
Border
+
+ {/* Rows */} +
+
Enabled
+
#141414 (Neutral Black)
+
Transparent
+
2px #141414 (Neutral Black)
+
+
+
Hover
+
#141414 (Neutral Black)
+
rgba(20, 20, 20, 0.15) (15% Black)
+
2px #141414 (Neutral Black)
+
+
+
Focus
+
#141414 (Neutral Black)
+
rgba(20, 20, 20, 0.15) (15% Black)
+
2px #141414 + 2px #141414 outline
+
+
+
Active
+
#141414 (Neutral Black)
+
Transparent
+
2px #141414 (Neutral Black)
+
+
+
Disabled
+
#A2A2A4 (Gray 400)
+
Transparent
+
2px #A2A2A4 (Gray 400)
+
+
+
+
+ + {/* Key Differences from Primary */} +
+
+

Key Differences from Primary

+
Comparison
+
+
+ {/* Header */} +
+
Aspect
+
Primary
+
Secondary
+
+ {/* Rows */} +
+
Background (Enabled)
+
Green 300 (#21E46B)
+
Transparent
+
+
+
Background (Hover)
+
Green 200 (#70EE97)
+
Green 100 (#EAFCF1)
+
+
+
Border (Enabled)
+
None
+
2px Green 400
+
+
+
Text Color (Enabled)
+
Black (#141414)
+
Green 400 (#0DAA3E)
+
+
+
Disabled Background
+
Gray 200 (#E0E0E1)
+
Transparent
+
+
+
Arrow Icon
+
✅ Shared
+
✅ Shared
+
+
+
+
+ ); +} diff --git a/about/button-showcase-tertiary.page.tsx b/about/button-showcase-tertiary.page.tsx new file mode 100644 index 0000000000..762887215c --- /dev/null +++ b/about/button-showcase-tertiary.page.tsx @@ -0,0 +1,561 @@ +import * as React from 'react'; +import { Button } from 'shared/components/Button'; +import { PageGrid, PageGridCol, PageGridRow } from 'shared/components/PageGrid/page-grid'; + +export const frontmatter = { + seo: { + title: 'BDS Tertiary Button Component Showcase', + description: 'Interactive showcase of the Brand Design System Tertiary Button component with all states and variants.', + }, +}; + +export default function ButtonShowcaseTertiary() { + const [clickCount, setClickCount] = React.useState(0); + + const handleClick = () => { + setClickCount((prev) => prev + 1); + }; + + return ( +
+
+
+

BDS Tertiary Button

+
Brand Design System
+
+

+ The Tertiary button is a text-only button style used for low-emphasis or contextual actions. It features no + background fill or border, appearing as a simple text link with optional arrow icon. This variant provides the + lowest visual emphasis while maintaining brand consistency through green text colors. +

+
+ + {/* Basic Usage */} +
+
+

Basic Usage

+
Tertiary Variant
+
+
+ + + +
+ {clickCount > 0 && ( +

+ Button clicked {clickCount} time{clickCount !== 1 ? 's' : ''} +

+ )} +
+ + {/* Primary vs Secondary vs Tertiary Comparison */} +
+
+

Primary vs Secondary vs Tertiary

+
Visual Hierarchy
+
+

+ Use Primary for main actions, Secondary for supporting actions, and Tertiary for low-emphasis or contextual + actions to create clear visual hierarchy. +

+
+ + + +
+
+ + {/* States */} +
+
+

Button States

+
Interactive States
+
+ + + +
+
Enabled State
+

Text-only style with green text color, no background or border.

+ +
+
+ +
+
Disabled State
+

Gray text indicates non-interactive state. Icon is hidden.

+ +
+
+
+
+
+
Hover & Focus States
+

+ Hover over the buttons or use Tab to focus them. Notice the underline appears and text color darkens to + Green 500. The focus state adds a green outline around the text. +

+
+ + +
+
+
+ + {/* Black Color Variant */} +
+
+

Black Color Variant

+
Color Theme
+
+

+ Tertiary buttons can use a black color theme with black text instead of green. +

+
+ + + +
+
+ + {/* Black Variant States */} +
+
+

Black Variant States

+
Interactive States
+
+ + + +
+
Enabled State
+

Black text with transparent background.

+ +
+
+ +
+
Disabled State
+

Same disabled styling as green variant.

+ +
+
+
+
+
+
Hover & Focus States
+

+ Hover over the buttons or use Tab to focus them. Notice the underline appears on hover/focus. +

+
+ + +
+
+
+ + {/* Green vs Black Comparison */} +
+
+

Green vs Black Comparison

+
Color Themes
+
+

Compare the green (default) and black color themes side by side.

+
+ + +
+
+ + {/* Without Icon */} +
+
+

Without Icon

+
Icon Control
+
+

Tertiary buttons can also be rendered without the arrow icon.

+
+ + +
+
+ + {/* Button Types */} +
+
+

Button Types

+
Form Integration
+
+

Tertiary buttons can be used for form actions like cancel or reset.

+
{ + e.preventDefault(); + alert('Form submitted!'); + }} + className="d-flex flex-wrap" + > + + + +
+
+ + {/* Responsive Behavior */} +
+
+

Responsive Behavior

+
Breakpoint Adjustments
+
+

+ Button padding adjusts automatically across breakpoints. Resize your browser window to see the changes: +

+ +
+ + +
+
+ + {/* Accessibility */} +
+
+

Accessibility Features

+
WCAG Compliance
+
+ + + +
Keyboard Navigation
+
    +
  • Tab to focus buttons
  • +
  • Enter or Space to activate
  • +
  • Focus indicator: 2px green outline (Green 500)
  • +
  • Disabled buttons are not focusable
  • +
+
+ +
Screen Reader Support
+
    +
  • Button labels are announced
  • +
  • Disabled state communicated via aria-disabled
  • +
  • Icons are hidden from screen readers (aria-hidden)
  • +
  • Semantic button element used
  • +
+
+
+
+
+
Color Contrast
+
    +
  • + Enabled: Green 400 (#0DAA3E) on White = 4.52:1 (AA for large text) +
  • +
  • + Hover/Focus: Green 500 (#078139) on White = 5.12:1 (AA) +
  • +
  • + Disabled: Gray 400 (#A2A2A4) on White = reduced contrast (acceptable for disabled state) +
  • +
+
+
+ + {/* Code Examples */} +
+
+

Code Examples

+
Implementation
+
+
+
+            {`import { Button } from 'shared/components/Button';
+
+// Basic tertiary button (green theme - default)
+
+
+// Black color theme
+
+
+// Disabled state
+
+
+// Without icon
+
+
+// Primary + Secondary + Tertiary pairing
+
+
+`}
+          
+
+
+ + {/* Design Specifications */} +
+
+

Design Specifications

+
Visual Details
+
+ + + +
Typography
+
    +
  • Font: Booton, sans-serif
  • +
  • Size: 18px (Body R token - different from Primary/Secondary)
  • +
  • Weight: 400
  • +
  • Line Height: 26.1px
  • +
  • Letter Spacing: -0.5px
  • +
+
+ +
Spacing & Layout
+
    +
  • Border Radius: 100px (fully rounded - inherited but not visually apparent)
  • +
  • Border: None
  • +
  • Background: Transparent
  • +
  • Icon Size: 15px × 14px
  • +
  • Icon Gap: 16px (default), 22px (hover/focus desktop), 21px (hover/focus mobile)
  • +
  • Max Height: 40px
  • +
+
+
+
+
+
State Colors - Green Theme
+
+ {/* Header */} +
+
State
+
Text Color
+
Background
+
Text Decoration
+
+ {/* Rows */} +
+
Enabled
+
#0DAA3E (Green 400)
+
Transparent
+
None
+
+
+
Hover
+
#078139 (Green 500)
+
Transparent
+
Underline
+
+
+
Focus
+
#078139 (Green 500)
+
Transparent
+
Underline + 2px Green 500 outline
+
+
+
Active
+
#0DAA3E (Green 400)
+
Transparent
+
Underline
+
+
+
Disabled
+
#A2A2A4 (Gray 400)
+
Transparent
+
None
+
+
+
+
+
State Colors - Black Theme
+
+ {/* Header */} +
+
State
+
Text Color
+
Background
+
Text Decoration
+
+ {/* Rows */} +
+
Enabled
+
#141414 (Neutral Black)
+
Transparent
+
None
+
+
+
Hover
+
#141414 (Neutral Black)
+
Transparent
+
Underline
+
+
+
Focus
+
#141414 (Neutral Black)
+
Transparent
+
Underline + 2px Black outline
+
+
+
Active
+
#141414 (Neutral Black)
+
Transparent
+
Underline
+
+
+
Disabled
+
#A2A2A4 (Gray 400)
+
Transparent
+
None
+
+
+
+
+ + {/* Key Differences from Primary/Secondary */} +
+
+

Key Differences from Primary/Secondary

+
Comparison
+
+
+ {/* Header */} +
+
Aspect
+
Primary
+
Secondary
+
Tertiary
+
+ {/* Rows */} +
+
Background (Enabled)
+
Green 300 (#21E46B)
+
Transparent
+
Transparent
+
+
+
Background (Hover)
+
Green 200 (#70EE97)
+
Green 100 (#EAFCF1)
+
Transparent
+
+
+
Border (Enabled)
+
None
+
2px Green 400
+
None
+
+
+
Text Color (Enabled)
+
Black (#141414)
+
Green 400 (#0DAA3E)
+
Green 400 (#0DAA3E)
+
+
+
Text Decoration
+
None
+
None
+
Underline (hover/focus/active)
+
+
+
Typography Token
+
Label R (16px)
+
Label R (16px)
+
Body R (18px)
+
+
+
Focus Indicator
+
2px Black border
+
2px Black outline
+
2px Green 500 outline
+
+
+
Disabled Background
+
Gray 200 (#E0E0E1)
+
Transparent
+
Transparent
+
+
+
Arrow Icon
+
✅ Shared
+
✅ Shared
+
✅ Shared
+
+
+
+
+ ); +} diff --git a/package.json b/package.json index 1834276faa..c604e72311 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,9 @@ "description": "The XRP Ledger Dev Portal is the authoritative source for XRP Ledger documentation, including the `rippled` server, client libraries, and other open-source XRP Ledger software.", "scripts": { "analyze-css": "node scripts/analyze-css.js", - "build-css": "sass --load-path styles/scss styles/xrpl.scss ./static/css/devportal2024-v1.tmp.css && NODE_ENV=production postcss ./static/css/devportal2024-v1.tmp.css -o ./static/css/devportal2024-v1.css && rm -f ./static/css/devportal2024-v1.tmp.css", - "build-css:dev": "sass --load-path styles/scss styles/xrpl.scss ./static/css/devportal2024-v1.tmp.css --source-map && postcss ./static/css/devportal2024-v1.tmp.css -o ./static/css/devportal2024-v1.css && rm -f ./static/css/devportal2024-v1.tmp.css", - "build-css:watch": "sass --watch --load-path styles/scss --silence-deprecation=import --silence-deprecation=global-builtin --silence-deprecation=color-functions styles/xrpl.scss ./static/css/devportal2024-v1.css --source-map", + "build-css": "sass --load-path styles/scss --load-path . styles/xrpl.scss ./static/css/devportal2024-v1.tmp.css && NODE_ENV=production postcss ./static/css/devportal2024-v1.tmp.css -o ./static/css/devportal2024-v1.css && rm -f ./static/css/devportal2024-v1.tmp.css", + "build-css:dev": "sass --load-path styles/scss --load-path . styles/xrpl.scss ./static/css/devportal2024-v1.tmp.css --source-map && postcss ./static/css/devportal2024-v1.tmp.css -o ./static/css/devportal2024-v1.css && rm -f ./static/css/devportal2024-v1.tmp.css", + "build-css:watch": "sass --watch --load-path styles/scss --load-path . styles/xrpl.scss:static/css/devportal2024-v1.css --source-map", "start": "realm develop" }, "keywords": [], diff --git a/shared/components/Button/Button.md b/shared/components/Button/Button.md new file mode 100644 index 0000000000..1364e36ce1 --- /dev/null +++ b/shared/components/Button/Button.md @@ -0,0 +1,459 @@ +# Button Component Documentation + +## Overview + +The Button component is a scalable, accessible button implementation following the XRPL Brand Design System (BDS). It supports three visual variants (Primary, Secondary, Tertiary) and two color themes (Green, Black), with comprehensive state management and smooth animations. + +## Features + +- **Three Variants**: Primary (solid), Secondary (outline), Tertiary (text-only) +- **Two Color Themes**: Green (default) and Black +- **Animated Arrow Icon**: Optional icon with smooth hover animations +- **Full State Support**: Enabled, Hover, Focus, Active, and Disabled states +- **Responsive Design**: Adaptive padding and spacing across breakpoints +- **Accessibility**: WCAG compliant with keyboard navigation and screen reader support +- **Smooth Animations**: 150ms transitions with custom bezier timing + +## Props API + +```typescript +interface ButtonProps { + /** Button variant - determines visual style */ + variant?: 'primary' | 'secondary' | 'tertiary'; + /** Color theme - green (default) or black */ + color?: 'green' | 'black'; + /** Button content/label */ + children: React.ReactNode; + /** Click handler */ + onClick?: () => void; + /** Disabled state */ + disabled?: boolean; + /** Button type attribute */ + type?: 'button' | 'submit' | 'reset'; + /** Additional CSS classes */ + className?: string; + /** Whether to show the arrow icon */ + showIcon?: boolean; +} +``` + +### Default Values + +- `variant`: `'primary'` +- `color`: `'green'` +- `disabled`: `false` +- `type`: `'button'` +- `className`: `''` +- `showIcon`: `true` + +## Variants + +### Primary Button + +The Primary button is used for the main call-to-action on a page. It features a solid background that fills from bottom-to-top on hover. + +**Visual Characteristics:** +- Solid background (Green 300 / Black) +- High visual emphasis +- Background color transitions on hover +- Black text on green background, white text on black background + +**Usage:** +```tsx + +``` + +### Secondary Button + +The Secondary button is used for supporting actions. It features an outline style with a transparent background that fills on hover. + +**Visual Characteristics:** +- Transparent background with 2px border +- Medium visual emphasis +- Background fills from bottom-to-top on hover +- Green/Black text and border + +**Usage:** +```tsx + +``` + +### Tertiary Button + +The Tertiary button is used for low-emphasis or contextual actions. It appears as text-only with optional underline on hover. + +**Visual Characteristics:** +- Text-only, no background or border +- Lowest visual emphasis +- Underline appears on hover/focus +- Different typography (Body R token vs Label R) + +**Usage:** +```tsx + +``` + +## Color Themes + +### Green Theme (Default) + +The green theme uses the XRPL brand green colors: +- **Primary**: Green 300 background (#21E46B), Green 200 hover (#70EE97) +- **Secondary**: Green 400 text/border (#0DAA3E), Green 500 hover (#078139) +- **Tertiary**: Green 400 text (#0DAA3E), Green 500 hover (#078139) + +### Black Theme + +The black theme provides an alternative color scheme: +- **Primary**: Black background (#141414), 80% black hover +- **Secondary**: Black text/border (#141414), 15% black hover fill +- **Tertiary**: Black text (#141414) + +**Usage:** +```tsx + +``` + +## States + +### Enabled State + +The default interactive state of the button. All variants display their base styling. + +### Hover State + +Triggered when the user hovers over the button with a mouse: +- **Primary/Secondary**: Background fills from bottom-to-top +- **Tertiary**: Underline appears, text color darkens +- **All Variants**: Arrow icon line shrinks, gap increases (with icon) + +### Focus State + +Triggered when the button receives keyboard focus (Tab key): +- Similar visual changes to hover state +- Additional focus outline (2px border/outline) +- Ensures keyboard accessibility + +### Active State + +Triggered when the button is being pressed: +- Returns to default padding/gap +- Background resets (for Primary/Secondary) +- Maintains visual feedback during press + +### Disabled State + +When `disabled={true}`: +- Icon is automatically hidden +- Gray text and background (Primary) or border (Secondary) +- Cursor changes to `not-allowed` +- `pointer-events: none` prevents interaction +- `aria-disabled` attribute set for screen readers + +**Usage:** +```tsx + +``` + +## How It Works + +### Component Structure + +The Button component uses BEM (Block Element Modifier) naming convention with the `bds` namespace: + +- `.bds-btn` - Base button class +- `.bds-btn--primary` - Primary variant modifier +- `.bds-btn--secondary` - Secondary variant modifier +- `.bds-btn--tertiary` - Tertiary variant modifier +- `.bds-btn--green` - Green color theme (default) +- `.bds-btn--black` - Black color theme +- `.bds-btn--disabled` - Disabled state modifier +- `.bds-btn--no-icon` - No icon modifier +- `.bds-btn__label` - Label element +- `.bds-btn__icon` - Icon container +- `.bds-btn__icon-line` - Arrow horizontal line +- `.bds-btn__icon-chevron` - Arrow chevron + +### Background Animation + +Primary and Secondary variants use a shared animation pattern: + +1. **Pseudo-element (`::before`)**: Creates the hover background fill +2. **Transform Origin**: Set to `bottom center` for bottom-to-top fill +3. **Initial State**: `scaleY(0)` - background hidden +4. **Hover/Focus**: `scaleY(1)` - background fills from bottom +5. **Active**: `scaleY(0)` - background resets during press + +This creates a smooth, directional fill animation that feels natural and responsive. + +### Arrow Icon Animation + +The arrow icon consists of two parts: +1. **Horizontal Line**: Shrinks from right to left (`scaleX(0)`) on hover/focus +2. **Chevron**: Stays visible, shifts right via increased gap + +The gap between label and icon increases on hover/focus: +- **Default**: 16px (desktop), 16px (mobile) +- **Hover/Focus**: 22px (desktop), 21px (mobile) + +This creates the illusion of the arrow "moving forward" as the line disappears. + +### Padding Adjustments + +On hover/focus, padding adjusts to accommodate the increased gap: +- **Primary**: `8px 19px 8px 20px` → `8px 13px 8px 20px` (desktop) +- **Secondary**: `6px 17px 6px 18px` → `6px 11px 6px 18px` (desktop) +- **Tertiary**: `8px 20px` → `8px 14px 8px 20px` (desktop) + +These adjustments maintain visual balance while allowing the icon animation to work smoothly. + +### Responsive Behavior + +The component adapts to screen size at the `1023px` breakpoint: + +**Desktop (≥1024px):** +- Larger padding values +- 22px gap on hover/focus + +**Tablet/Mobile (≤1023px):** +- Reduced padding values +- 21px gap on hover/focus + +All transitions remain smooth across breakpoints. + +## Typography + +### Primary & Secondary Variants +- **Font**: Booton, sans-serif +- **Size**: 16px (Label R token) +- **Weight**: 400 +- **Line Height**: 23.2px +- **Letter Spacing**: 0px + +### Tertiary Variant +- **Font**: Booton, sans-serif +- **Size**: 18px (Body R token) +- **Weight**: 400 +- **Line Height**: 26.1px +- **Letter Spacing**: -0.5px + +## Spacing & Layout + +- **Border Radius**: 100px (fully rounded) +- **Max Height**: 40px +- **Icon Size**: 15px × 14px +- **Transitions**: 150ms with `cubic-bezier(0.98, 0.12, 0.12, 0.98)` + +## Usage Examples + +### Basic Usage + +```tsx +import { Button } from 'shared/components/Button'; + +// Primary button (default) + + +// Secondary button + + +// Tertiary button + +``` + +### Form Integration + +```tsx +
+ + + +
+``` + +### Without Icon + +```tsx + +``` + +### Disabled State + +```tsx + +``` + +### Color Themes + +```tsx +{/* Green theme (default) */} + + +{/* Black theme */} + +``` + +### Visual Hierarchy + +```tsx +{/* Use variants to create clear visual hierarchy */} + + + +``` + +## Accessibility + +### Keyboard Navigation + +- **Tab**: Focus next button +- **Shift+Tab**: Focus previous button +- **Enter/Space**: Activate button +- **Focus Indicator**: Visible outline/border (2px) +- **Disabled buttons**: Not focusable + +### Screen Reader Support + +- Semantic ` + * + * + * + */ +/** + * Helper function to extract text content from ReactNode + */ +const getTextFromChildren = (children: React.ReactNode): string => { + if (typeof children === 'string' || typeof children === 'number') { + return String(children); + } + if (Array.isArray(children)) { + return children.map(getTextFromChildren).join(''); + } + if (React.isValidElement(children)) { + const props = children.props as { children?: React.ReactNode }; + if (props.children) { + return getTextFromChildren(props.children); + } + } + return ''; +}; + +export const Button: React.FC = ({ + variant = 'primary', + color = 'green', + children, + onClick, + disabled = false, + type = 'button', + className = '', + showIcon = true, + ariaLabel, +}) => { + // Hide icon when disabled (per design spec) + const shouldShowIcon = showIcon && !disabled; + + // Default ariaLabel to button text if not provided + const buttonAriaLabel = ariaLabel || getTextFromChildren(children); + + // Build class names using BEM with bds namespace + const classNames = [ + 'bds-btn', + `bds-btn--${variant}`, + `bds-btn--${color}`, + disabled ? 'bds-btn--disabled' : '', + !shouldShowIcon ? 'bds-btn--no-icon' : '', + className, + ] + .filter(Boolean) + .join(' '); + + return ( + + ); +}; + +export default Button; diff --git a/shared/components/Button/index.ts b/shared/components/Button/index.ts new file mode 100644 index 0000000000..b24e6ed312 --- /dev/null +++ b/shared/components/Button/index.ts @@ -0,0 +1,2 @@ +export { Button, type ButtonProps } from './Button'; +export { default } from './Button'; diff --git a/static/css/devportal2024-v1.css b/static/css/devportal2024-v1.css index 9076088222..8fa10f8f80 100644 --- a/static/css/devportal2024-v1.css +++ b/static/css/devportal2024-v1.css @@ -11145,6 +11145,591 @@ button[disabled=disabled] { right: 0; } +.bds-btn { + display: inline-flex; + align-items: center; + justify-content: center; + max-height: 40px; + font-family: "Booton", sans-serif; + font-size: 16px; + font-weight: 400; + line-height: 23.2px; + letter-spacing: 0px; + white-space: nowrap; + border: none; + border-radius: 100px; + cursor: pointer; + position: relative; + overflow: hidden; + z-index: 1; + transition: color 150ms cubic-bezier(0.98, 0.12, 0.12, 0.98), border-color 150ms cubic-bezier(0.98, 0.12, 0.12, 0.98), padding 150ms cubic-bezier(0.98, 0.12, 0.12, 0.98), gap 150ms cubic-bezier(0.98, 0.12, 0.12, 0.98), transform 150ms cubic-bezier(0.98, 0.12, 0.12, 0.98); +} +.bds-btn::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: -1; + transform: scaleY(0); + transform-origin: bottom center; + transition: transform 150ms cubic-bezier(0.98, 0.12, 0.12, 0.98); +} +.bds-btn:hover:not(:disabled):not(.bds-btn--disabled)::before, .bds-btn:focus-visible:not(:disabled):not(.bds-btn--disabled)::before { + transform: scaleY(1); +} +.bds-btn:active:not(:disabled):not(.bds-btn--disabled)::before { + transform: scaleY(0); +} +.bds-btn__label { + flex-shrink: 0; + position: relative; + z-index: 1; +} +.bds-btn__icon { + width: 15px; + height: 14px; + flex-shrink: 0; + transition: opacity 150ms cubic-bezier(0.98, 0.12, 0.12, 0.98); + color: currentColor; + overflow: visible; + position: relative; + z-index: 1; +} +.bds-btn__icon-line { + transform-box: fill-box; + transform-origin: right center; + transform: scaleX(1); + transition: transform 150ms cubic-bezier(0.98, 0.12, 0.12, 0.98); +} +.bds-btn__icon-chevron { + transition: transform 150ms cubic-bezier(0.98, 0.12, 0.12, 0.98); +} +.bds-btn:hover:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-line, .bds-btn:focus-visible:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-line { + transform: scaleX(0); +} +@media (max-width: 1279.98px) { + .bds-btn { + font-size: 14px; + line-height: 20.1px; + } +} + +.bds-btn--primary { + color: #141414; + background-color: #21E46B; +} +.bds-btn--primary::before { + background-color: #70EE97; +} +.bds-btn--primary { + padding: 8px 19px 8px 20px; + gap: 16px; +} +.bds-btn--primary.bds-btn--no-icon { + padding: 8px 20px; +} +.bds-btn--primary:hover:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) { + padding: 8px 13px 8px 20px; + gap: 22px; +} +.bds-btn--primary:focus-visible:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) { + outline: 2px solid #141414; + outline-offset: 2px; + padding: 8px 13px 8px 20px; + gap: 22px; +} +.bds-btn--primary:focus-visible:not(:disabled):not(.bds-btn--disabled).bds-btn--no-icon { + outline: 2px solid #141414; + outline-offset: 2px; + padding: 8px 20px; +} +.bds-btn--primary:active:not(:disabled):not(.bds-btn--disabled) { + padding: 8px 19px 8px 20px; + gap: 16px; +} +.bds-btn--primary:disabled, .bds-btn--primary.bds-btn--disabled { + color: #838386; + background-color: #E0E0E1; + cursor: not-allowed; + pointer-events: none; +} +.bds-btn--primary:disabled::before, .bds-btn--primary.bds-btn--disabled::before { + display: none; +} +@media (max-width: 1279.98px) { + .bds-btn--primary { + padding: 8px 15px 8px 16px; + gap: 16px; + } + .bds-btn--primary.bds-btn--no-icon { + padding: 8px 16px; + } + .bds-btn--primary:hover:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) { + padding: 8px 10px 8px 16px; + gap: 21px; + } + .bds-btn--primary:focus-visible:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) { + outline: 2px solid #141414; + outline-offset: 2px; + padding: 8px 10px 8px 16px; + gap: 21px; + } + .bds-btn--primary:focus-visible:not(:disabled):not(.bds-btn--disabled).bds-btn--no-icon { + outline: 2px solid #141414; + outline-offset: 2px; + padding: 8px 16px; + } + .bds-btn--primary:active:not(:disabled):not(.bds-btn--disabled) { + padding: 8px 15px 8px 16px; + gap: 16px; + } +} + +.bds-btn--secondary { + color: #0DAA3E; + background-color: transparent; + border: 2px solid #0DAA3E; +} +.bds-btn--secondary::before { + background-color: #EAFCF1; +} +.bds-btn--secondary { + padding: 6px 17px 6px 18px; + gap: 16px; +} +.bds-btn--secondary.bds-btn--no-icon { + padding: 6px 18px; +} +.bds-btn--secondary:hover:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) { + color: #078139; + border-color: #078139; + padding: 6px 11px 6px 18px; + gap: 22px; +} +.bds-btn--secondary:hover:not(:disabled):not(.bds-btn--disabled).bds-btn--no-icon { + color: #078139; + border-color: #078139; +} +.bds-btn--secondary:focus-visible:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) { + color: #078139; + border: none; + outline: 2px solid #141414; + outline-offset: 2px; + padding: 6px 13px 6px 20px; + gap: 22px; +} +.bds-btn--secondary:focus-visible:not(:disabled):not(.bds-btn--disabled).bds-btn--no-icon { + color: #078139; + border: none; + outline: 2px solid #141414; + outline-offset: 2px; + padding: 6px 20px; +} +.bds-btn--secondary:active:not(:disabled):not(.bds-btn--disabled) { + color: #0DAA3E; + border-color: #0DAA3E; + padding: 6px 17px 6px 18px; + gap: 16px; +} +.bds-btn--secondary:disabled, .bds-btn--secondary.bds-btn--disabled { + color: #A2A2A4; + background-color: transparent; + border-color: #A2A2A4; + cursor: not-allowed; + pointer-events: none; +} +.bds-btn--secondary:disabled::before, .bds-btn--secondary.bds-btn--disabled::before { + display: none; +} +@media (max-width: 1279.98px) { + .bds-btn--secondary { + padding: 6px 13px 6px 14px; + gap: 16px; + } + .bds-btn--secondary.bds-btn--no-icon { + padding: 6px 14px; + } + .bds-btn--secondary:hover:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) { + padding: 6px 8px 6px 14px; + gap: 21px; + } + .bds-btn--secondary:focus-visible:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) { + border: none; + outline: 2px solid #141414; + outline-offset: 2px; + padding: 6px 10px 6px 16px; + gap: 21px; + } + .bds-btn--secondary:focus-visible:not(:disabled):not(.bds-btn--disabled).bds-btn--no-icon { + border: none; + outline: 2px solid #141414; + outline-offset: 2px; + padding: 6px 16px; + } + .bds-btn--secondary:active:not(:disabled):not(.bds-btn--disabled) { + padding: 6px 13px 6px 14px; + gap: 16px; + } +} + +.bds-btn--black.bds-btn--primary { + color: #FFFFFF; + background-color: #141414; +} +.bds-btn--black.bds-btn--primary::before { + background-color: rgba(20, 20, 20, 0.8); +} +.bds-btn--black.bds-btn--primary:hover:not(:disabled):not(.bds-btn--disabled) { + color: #FFFFFF; +} +.bds-btn--black.bds-btn--primary:focus-visible:not(:disabled):not(.bds-btn--disabled) { + color: #FFFFFF; + outline: 2px solid #141414; + outline-offset: 2px; +} +.bds-btn--black.bds-btn--primary:active:not(:disabled):not(.bds-btn--disabled) { + color: #FFFFFF; +} +.bds-btn--black.bds-btn--primary:disabled, .bds-btn--black.bds-btn--primary.bds-btn--disabled { + color: #838386; + background-color: #E0E0E1; +} +.bds-btn--black.bds-btn--secondary { + color: #141414; + border-color: #141414; +} +.bds-btn--black.bds-btn--secondary::before { + background-color: rgba(20, 20, 20, 0.15); +} +.bds-btn--black.bds-btn--secondary:hover:not(:disabled):not(.bds-btn--disabled) { + color: #141414; + border-color: #141414; +} +.bds-btn--black.bds-btn--secondary:focus-visible:not(:disabled):not(.bds-btn--disabled) { + color: #141414; + border: none; + outline: 2px solid #141414; + outline-offset: 2px; +} +.bds-btn--black.bds-btn--secondary:active:not(:disabled):not(.bds-btn--disabled) { + color: #141414; + border-color: #141414; +} +.bds-btn--black.bds-btn--secondary:disabled, .bds-btn--black.bds-btn--secondary.bds-btn--disabled { + color: #838386; + border-color: #A2A2A4; +} +.bds-btn--black.bds-btn--tertiary { + color: #141414; +} +.bds-btn--black.bds-btn--tertiary:hover:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) { + color: #141414 !important; + text-decoration: underline; + padding: 8px 14px 8px 20px; + gap: 22px; +} +.bds-btn--black.bds-btn--tertiary:hover:not(:disabled):not(.bds-btn--disabled).bds-btn--no-icon { + color: #141414 !important; + text-decoration: underline; +} +.bds-btn--black.bds-btn--tertiary:focus-visible:not(:disabled):not(.bds-btn--disabled) { + color: #141414 !important; + text-decoration: underline; + border-radius: 0; + outline: 2px solid #141414; + outline-offset: -2px; +} +.bds-btn--black.bds-btn--tertiary:active:not(:disabled):not(.bds-btn--disabled) { + color: #141414 !important; + text-decoration: underline; +} +.bds-btn--black.bds-btn--tertiary:disabled, .bds-btn--black.bds-btn--tertiary.bds-btn--disabled { + color: #838386; +} +@media (max-width: 1279.98px) { + .bds-btn--black.bds-btn--tertiary:hover:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) { + color: #141414 !important; + padding: 8px 11px 8px 16px; + gap: 21px; + } +} + +.bds-btn--tertiary { + color: #0DAA3E; + background-color: transparent; + border: none; + text-decoration: none; + font-size: 18px; + line-height: 26.1px; + letter-spacing: -0.5px; + padding: 8px 20px; + gap: 16px; +} +.bds-btn--tertiary.bds-btn--no-icon { + padding: 8px 20px; +} +.bds-btn--tertiary::before { + display: none; +} +.bds-btn--tertiary:hover:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) { + color: #078139; + text-decoration: underline; + padding: 8px 14px 8px 20px; + gap: 22px; +} +.bds-btn--tertiary:hover:not(:disabled):not(.bds-btn--disabled).bds-btn--no-icon { + color: #078139; + text-decoration: underline; +} +.bds-btn--tertiary:focus-visible:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) { + color: #078139; + text-decoration: underline; + border-radius: 0; + outline: 2px solid #141414; + outline-offset: -2px; + padding: 5px 2px 2px; + gap: 22px; +} +.bds-btn--tertiary:focus-visible:not(:disabled):not(.bds-btn--disabled).bds-btn--no-icon { + color: #078139; + text-decoration: underline; + border-radius: 0; + outline: 2px solid #141414; + outline-offset: -2px; + padding: 5px 2px 2px; +} +.bds-btn--tertiary:active:not(:disabled):not(.bds-btn--disabled) { + color: #0DAA3E; + text-decoration: underline; + padding: 8px 20px; + gap: 16px; +} +.bds-btn--tertiary:disabled, .bds-btn--tertiary.bds-btn--disabled { + color: #A2A2A4; + background-color: transparent; + text-decoration: none; + cursor: not-allowed; + pointer-events: none; +} +.bds-btn--tertiary:disabled::before, .bds-btn--tertiary.bds-btn--disabled::before { + display: none; +} +@media (max-width: 1279.98px) { + .bds-btn--tertiary { + font-size: 16px; + line-height: 23.2px; + letter-spacing: 0px; + padding: 8px 16px; + gap: 16px; + } + .bds-btn--tertiary.bds-btn--no-icon { + padding: 8px 16px; + } + .bds-btn--tertiary:hover:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) { + padding: 8px 11px 8px 16px; + gap: 21px; + } + .bds-btn--tertiary:focus-visible:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) { + color: #078139; + text-decoration: underline; + border-radius: 0; + outline: 2px solid #141414; + outline-offset: -2px; + padding: 5px 2px 2px; + gap: 21px; + } + .bds-btn--tertiary:focus-visible:not(:disabled):not(.bds-btn--disabled).bds-btn--no-icon { + color: #078139; + text-decoration: underline; + border-radius: 0; + outline: 2px solid #141414; + outline-offset: -2px; + padding: 5px 2px 2px; + } + .bds-btn--tertiary:active:not(:disabled):not(.bds-btn--disabled) { + padding: 8px 16px; + gap: 16px; + } +} + +html.dark .bds-btn--primary:focus-visible:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) { + outline: 2px solid #FFFFFF; + outline-offset: 2px; + padding: 8px 13px 8px 20px; + gap: 22px; +} +html.dark .bds-btn--primary:focus-visible:not(:disabled):not(.bds-btn--disabled).bds-btn--no-icon { + outline: 2px solid #FFFFFF; + outline-offset: 2px; + padding: 8px 20px; +} +@media (max-width: 1279.98px) { + html.dark .bds-btn--primary:focus-visible:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) { + outline: 2px solid #FFFFFF; + outline-offset: 2px; + padding: 8px 10px 8px 16px; + gap: 21px; + } + html.dark .bds-btn--primary:focus-visible:not(:disabled):not(.bds-btn--disabled).bds-btn--no-icon { + outline: 2px solid #FFFFFF; + outline-offset: 2px; + padding: 8px 16px; + } +} +html.dark .bds-btn--secondary:not(.bds-btn--black) { + color: #21E46B; + border-color: #21E46B; +} +html.dark .bds-btn--secondary:not(.bds-btn--black)::before { + background-color: #078139; +} +html.dark .bds-btn--secondary:not(.bds-btn--black):hover:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) { + color: #70EE97; + border-color: #70EE97; + padding: 6px 11px 6px 18px; + gap: 22px; +} +html.dark .bds-btn--secondary:not(.bds-btn--black):hover:not(:disabled):not(.bds-btn--disabled).bds-btn--no-icon { + color: #70EE97; + border-color: #70EE97; +} +html.dark .bds-btn--secondary:not(.bds-btn--black):focus-visible:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) { + color: #70EE97; + border: none; + outline: 2px solid #FFFFFF; + outline-offset: 2px; + padding: 6px 13px 6px 20px; + gap: 22px; +} +html.dark .bds-btn--secondary:not(.bds-btn--black):focus-visible:not(:disabled):not(.bds-btn--disabled).bds-btn--no-icon { + color: #70EE97; + border: none; + outline: 2px solid #FFFFFF; + outline-offset: 2px; + padding: 6px 20px; +} +html.dark .bds-btn--secondary:not(.bds-btn--black):active:not(:disabled):not(.bds-btn--disabled) { + color: #21E46B; + border-color: #21E46B; + padding: 6px 17px 6px 18px; + gap: 16px; +} +html.dark .bds-btn--secondary:not(.bds-btn--black):disabled, html.dark .bds-btn--secondary:not(.bds-btn--black).bds-btn--disabled { + color: #A2A2A4; + background-color: transparent; + border-color: #A2A2A4; + cursor: not-allowed; + pointer-events: none; +} +html.dark .bds-btn--secondary:not(.bds-btn--black):disabled::before, html.dark .bds-btn--secondary:not(.bds-btn--black).bds-btn--disabled::before { + display: none; +} +@media (max-width: 1279.98px) { + html.dark .bds-btn--secondary:not(.bds-btn--black):hover:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) { + color: #70EE97; + border-color: #70EE97; + padding: 6px 8px 6px 14px; + gap: 21px; + } + html.dark .bds-btn--secondary:not(.bds-btn--black):focus-visible:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) { + color: #70EE97; + border: none; + outline: 2px solid #FFFFFF; + outline-offset: 2px; + padding: 6px 10px 6px 16px; + gap: 21px; + } + html.dark .bds-btn--secondary:not(.bds-btn--black):focus-visible:not(:disabled):not(.bds-btn--disabled).bds-btn--no-icon { + color: #70EE97; + border: none; + outline: 2px solid #FFFFFF; + outline-offset: 2px; + padding: 6px 16px; + } + html.dark .bds-btn--secondary:not(.bds-btn--black):active:not(:disabled):not(.bds-btn--disabled) { + color: #21E46B; + border-color: #21E46B; + padding: 6px 13px 6px 14px; + gap: 16px; + } +} +html.dark .bds-btn--tertiary:not(.bds-btn--black) { + color: #21E46B; +} +html.dark .bds-btn--tertiary:not(.bds-btn--black):hover:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) { + color: #70EE97; + text-decoration: underline; + padding: 8px 14px 8px 20px; + gap: 22px; +} +html.dark .bds-btn--tertiary:not(.bds-btn--black):hover:not(:disabled):not(.bds-btn--disabled).bds-btn--no-icon { + color: #70EE97; + text-decoration: underline; +} +html.dark .bds-btn--tertiary:not(.bds-btn--black):focus-visible:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) { + color: #70EE97; + text-decoration: underline; + border-radius: 0; + outline: 2px solid #FFFFFF; + outline-offset: -2px; + padding: 5px 2px 2px; + gap: 22px; +} +html.dark .bds-btn--tertiary:not(.bds-btn--black):focus-visible:not(:disabled):not(.bds-btn--disabled).bds-btn--no-icon { + color: #70EE97; + text-decoration: underline; + border-radius: 0; + outline: 2px solid #FFFFFF; + outline-offset: -2px; + padding: 5px 2px 2px; +} +html.dark .bds-btn--tertiary:not(.bds-btn--black):active:not(:disabled):not(.bds-btn--disabled) { + color: #21E46B; + text-decoration: underline; + padding: 8px 20px; + gap: 16px; +} +html.dark .bds-btn--tertiary:not(.bds-btn--black):disabled, html.dark .bds-btn--tertiary:not(.bds-btn--black).bds-btn--disabled { + color: #A2A2A4; + background-color: transparent; + text-decoration: none; + cursor: not-allowed; + pointer-events: none; +} +@media (max-width: 1279.98px) { + html.dark .bds-btn--tertiary:not(.bds-btn--black):hover:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) { + color: #70EE97; + text-decoration: underline; + padding: 8px 11px 8px 16px; + gap: 21px; + } + html.dark .bds-btn--tertiary:not(.bds-btn--black):focus-visible:not(:disabled):not(.bds-btn--disabled):not(.bds-btn--no-icon) { + color: #70EE97; + text-decoration: underline; + border-radius: 0; + outline: 2px solid #FFFFFF; + outline-offset: -2px; + padding: 5px 2px 2px; + gap: 21px; + } + html.dark .bds-btn--tertiary:not(.bds-btn--black):focus-visible:not(:disabled):not(.bds-btn--disabled).bds-btn--no-icon { + color: #70EE97; + text-decoration: underline; + border-radius: 0; + outline: 2px solid #FFFFFF; + outline-offset: -2px; + padding: 5px 2px 2px; + } + html.dark .bds-btn--tertiary:not(.bds-btn--black):active:not(:disabled):not(.bds-btn--disabled) { + color: #21E46B; + text-decoration: underline; + padding: 8px 16px; + gap: 16px; + } +} + /* TABLE STYLING */ article table { clear: right; diff --git a/static/img/icons/button/button-arrow-hovered.svg b/static/img/icons/button/button-arrow-hovered.svg new file mode 100644 index 0000000000..e08a6f4a5a --- /dev/null +++ b/static/img/icons/button/button-arrow-hovered.svg @@ -0,0 +1,3 @@ + + + diff --git a/static/img/icons/button/button-arrow-right.svg b/static/img/icons/button/button-arrow-right.svg new file mode 100644 index 0000000000..550a0dcc6a --- /dev/null +++ b/static/img/icons/button/button-arrow-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/styles/_breakpoints.scss b/styles/_breakpoints.scss new file mode 100644 index 0000000000..79c89a8c30 --- /dev/null +++ b/styles/_breakpoints.scss @@ -0,0 +1,10 @@ +// Shared breakpoints for responsive design +// These values are used by Bootstrap and custom components + +$grid-breakpoints: ( + xs: 0, + sm: 576px, + md: 576px, + lg: 992px, + xl: 1280px +); diff --git a/styles/xrpl.scss b/styles/xrpl.scss index 2549d24255..83833a6a52 100644 --- a/styles/xrpl.scss +++ b/styles/xrpl.scss @@ -40,13 +40,7 @@ $font-family-sans-serif: "Booton", "Noto Sans", -apple-system, BlinkMacSystemFon $base-size: 16px; $line-height-base: 1.5; -$grid-breakpoints: ( - xs: 0, - sm: 576px, - md: 576px, - lg: 992px, - xl: 1280px -); +@import "_breakpoints.scss"; // Bootstrap v5 - Import only what we need // Note: Bootstrap still uses @import internally, so we use @import for Bootstrap @@ -86,6 +80,7 @@ $grid-breakpoints: ( @import "_side-nav.scss"; @import "_helpers.scss"; @import "_buttons.scss"; +@import "../shared/components/Button/Button.scss"; @import "_tables.scss"; @import "_use-cases.scss"; @import "_github-edit.scss"; @@ -132,4 +127,4 @@ $grid-breakpoints: ( html.light { @import "light/_light-theme.scss"; -} +} \ No newline at end of file