Merge pull request #3405 from XRPLF/component/divider

Add Divider component and documentation
This commit is contained in:
Aria Keshmiri
2025-12-04 15:34:04 -08:00
committed by GitHub
7 changed files with 1132 additions and 0 deletions

View File

@@ -0,0 +1,465 @@
import * as React from "react";
import { PageGrid, PageGridRow, PageGridCol } from "shared/components/PageGrid/page-grid";
import { Divider } from "shared/components/Divider";
export const frontmatter = {
seo: {
title: 'Divider Component Showcase',
description: "A comprehensive showcase of all Divider component variants, weights, colors, and orientations in the XRPL.org Design System.",
}
};
export default function DividerShowcase() {
return (
<div className="landing">
<div className="overflow-hidden">
<section className="py-26 text-center">
<div className="col-lg-8 mx-auto">
<h6 className="eyebrow mb-3">Component Showcase</h6>
<h1 className="mb-4">Divider Component</h1>
<p className="longform">
A comprehensive showcase of all Divider component variants, weights, colors, and orientations.
</p>
</div>
</section>
{/* Weight by Color Matrix - Horizontal */}
<PageGrid className="py-26">
<PageGridRow>
<PageGridCol span={12}>
<h2 className="h4 mb-6">Horizontal Dividers: Weight by Color Matrix</h2>
<div className="mb-10">
{/* Header Row */}
<div className="d-flex flex-row mb-4" style={{ gap: '2rem' }}>
<div style={{ width: '120px', flexShrink: 0 }}>
<h6 className="mb-0">Weight</h6>
</div>
<div style={{ flex: '1 1 0', minWidth: 0 }}>
<h6 className="mb-0">Gray</h6>
</div>
<div style={{ flex: '1 1 0', minWidth: 0 }}>
<h6 className="mb-0">Base</h6>
</div>
<div style={{ flex: '1 1 0', minWidth: 0 }}>
<h6 className="mb-0">Green</h6>
</div>
</div>
{/* Thin Row */}
<div className="d-flex flex-row mb-5 align-items-center" style={{ gap: '2rem' }}>
<div style={{ width: '120px', flexShrink: 0 }}>
<strong>Thin</strong>
<br />
<small className="text-muted">0.5px</small>
</div>
<div style={{ flex: '1 1 0', minWidth: 0 }}>
<Divider weight="thin" color="gray" />
</div>
<div style={{ flex: '1 1 0', minWidth: 0 }}>
<Divider weight="thin" color="base" />
</div>
<div style={{ flex: '1 1 0', minWidth: 0 }}>
<Divider weight="thin" color="green" />
</div>
</div>
{/* Regular Row */}
<div className="d-flex flex-row mb-5 align-items-center" style={{ gap: '2rem' }}>
<div style={{ width: '120px', flexShrink: 0 }}>
<strong>Regular</strong>
<br />
<small className="text-muted">1px</small>
</div>
<div style={{ flex: '1 1 0', minWidth: 0 }}>
<Divider weight="regular" color="gray" />
</div>
<div style={{ flex: '1 1 0', minWidth: 0 }}>
<Divider weight="regular" color="base" />
</div>
<div style={{ flex: '1 1 0', minWidth: 0 }}>
<Divider weight="regular" color="green" />
</div>
</div>
{/* Strong Row */}
<div className="d-flex flex-row align-items-center" style={{ gap: '2rem' }}>
<div style={{ width: '120px', flexShrink: 0 }}>
<strong>Strong</strong>
<br />
<small className="text-muted">2px</small>
</div>
<div style={{ flex: '1 1 0', minWidth: 0 }}>
<Divider weight="strong" color="gray" />
</div>
<div style={{ flex: '1 1 0', minWidth: 0 }}>
<Divider weight="strong" color="base" />
</div>
<div style={{ flex: '1 1 0', minWidth: 0 }}>
<Divider weight="strong" color="green" />
</div>
</div>
</div>
</PageGridCol>
</PageGridRow>
</PageGrid>
{/* Vertical Dividers */}
<PageGrid className="py-26">
<PageGridRow>
<PageGridCol span={12}>
<h2 className="h4 mb-6">Vertical Dividers: Weight by Color Matrix</h2>
<div className="mb-10">
{/* Header Row */}
<div className="d-flex flex-row mb-4" style={{ gap: '2rem' }}>
<div style={{ width: '120px', flexShrink: 0 }}>
<h6 className="mb-0">Weight</h6>
</div>
<div style={{ flex: '1 1 0', minWidth: 0 }}>
<h6 className="mb-0">Gray</h6>
</div>
<div style={{ flex: '1 1 0', minWidth: 0 }}>
<h6 className="mb-0">Base</h6>
</div>
<div style={{ flex: '1 1 0', minWidth: 0 }}>
<h6 className="mb-0">Green</h6>
</div>
</div>
{/* Thin Row */}
<div className="d-flex flex-row mb-5 align-items-stretch" style={{ gap: '2rem', height: '120px' }}>
<div style={{ width: '120px', flexShrink: 0 }} className="d-flex align-items-center">
<div>
<strong>Thin</strong>
<br />
<small className="text-muted">0.5px</small>
</div>
</div>
<div style={{ flex: '1 1 0', minWidth: 0 }} className="d-flex justify-content-center">
<Divider orientation="vertical" weight="thin" color="gray" />
</div>
<div style={{ flex: '1 1 0', minWidth: 0 }} className="d-flex justify-content-center">
<Divider orientation="vertical" weight="thin" color="base" />
</div>
<div style={{ flex: '1 1 0', minWidth: 0 }} className="d-flex justify-content-center">
<Divider orientation="vertical" weight="thin" color="green" />
</div>
</div>
{/* Regular Row */}
<div className="d-flex flex-row mb-5 align-items-stretch" style={{ gap: '2rem', height: '120px' }}>
<div style={{ width: '120px', flexShrink: 0 }} className="d-flex align-items-center">
<div>
<strong>Regular</strong>
<br />
<small className="text-muted">1px</small>
</div>
</div>
<div style={{ flex: '1 1 0', minWidth: 0 }} className="d-flex justify-content-center">
<Divider orientation="vertical" weight="regular" color="gray" />
</div>
<div style={{ flex: '1 1 0', minWidth: 0 }} className="d-flex justify-content-center">
<Divider orientation="vertical" weight="regular" color="base" />
</div>
<div style={{ flex: '1 1 0', minWidth: 0 }} className="d-flex justify-content-center">
<Divider orientation="vertical" weight="regular" color="green" />
</div>
</div>
{/* Strong Row */}
<div className="d-flex flex-row align-items-stretch" style={{ gap: '2rem', height: '120px' }}>
<div style={{ width: '120px', flexShrink: 0 }} className="d-flex align-items-center">
<div>
<strong>Strong</strong>
<br />
<small className="text-muted">2px</small>
</div>
</div>
<div style={{ flex: '1 1 0', minWidth: 0 }} className="d-flex justify-content-center">
<Divider orientation="vertical" weight="strong" color="gray" />
</div>
<div style={{ flex: '1 1 0', minWidth: 0 }} className="d-flex justify-content-center">
<Divider orientation="vertical" weight="strong" color="base" />
</div>
<div style={{ flex: '1 1 0', minWidth: 0 }} className="d-flex justify-content-center">
<Divider orientation="vertical" weight="strong" color="green" />
</div>
</div>
</div>
</PageGridCol>
</PageGridRow>
</PageGrid>
{/* Weights Comparison */}
<PageGrid className="py-26">
<PageGridRow>
<PageGridCol span={12}>
<h2 className="h4 mb-6">Stroke Weights</h2>
<p className="mb-4">Dividers are available in three stroke weights to represent different levels of visual hierarchy.</p>
<div className="d-flex flex-column gap-5 mb-10">
<div>
<h6 className="mb-3">Thin (0.5px) - Subtle separation</h6>
<Divider weight="thin" />
</div>
<div>
<h6 className="mb-3">Regular (1px) - Default weight</h6>
<Divider weight="regular" />
</div>
<div>
<h6 className="mb-3">Strong (2px) - Emphasized boundaries</h6>
<Divider weight="strong" />
</div>
</div>
</PageGridCol>
</PageGridRow>
</PageGrid>
{/* Color Variants */}
<PageGrid className="py-26">
<PageGridRow>
<PageGridCol span={12}>
<h2 className="h4 mb-6">Color Variants</h2>
<p className="mb-4">Colors are mapped from the XRPL Design System color palette:</p>
<div className="d-flex flex-row gap-6 mb-6" style={{ flexWrap: 'wrap' }}>
{/* Dark Mode Colors (Default) */}
<div style={{ flex: '1 1 300px', minWidth: '280px' }}>
<h6 className="mb-3">Dark Mode (Default)</h6>
<div className="d-flex flex-column gap-3">
<div className="d-flex flex-row align-items-center gap-3">
<div style={{ width: '40px', height: '40px', backgroundColor: '#454549', borderRadius: '4px', flexShrink: 0, border: '1px solid var(--bs-border-color, #dee2e6)' }}></div>
<div>
<strong>Gray:</strong> <code>$gray-600</code>
<br />
<small className="text-muted">#454549</small>
</div>
</div>
<div className="d-flex flex-row align-items-center gap-3">
<div style={{ width: '40px', height: '40px', backgroundColor: '#FFFFFF', borderRadius: '4px', flexShrink: 0, border: '1px solid var(--bs-border-color, #dee2e6)' }}></div>
<div>
<strong>Base:</strong> <code>$white</code>
<br />
<small className="text-muted">#FFFFFF</small>
</div>
</div>
<div className="d-flex flex-row align-items-center gap-3">
<div style={{ width: '40px', height: '40px', backgroundColor: '#21E46B', borderRadius: '4px', flexShrink: 0, border: '1px solid var(--bs-border-color, #dee2e6)' }}></div>
<div>
<strong>Green:</strong> <code>$green-300</code>
<br />
<small className="text-muted">#21E46B</small>
</div>
</div>
</div>
</div>
{/* Light Mode Colors */}
<div style={{ flex: '1 1 300px', minWidth: '280px' }}>
<h6 className="mb-3">Light Mode</h6>
<div className="d-flex flex-column gap-3">
<div className="d-flex flex-row align-items-center gap-3">
<div style={{ width: '40px', height: '40px', backgroundColor: '#C1C1C2', borderRadius: '4px', flexShrink: 0, border: '1px solid var(--bs-border-color, #dee2e6)' }}></div>
<div>
<strong>Gray:</strong> <code>$gray-300</code>
<br />
<small className="text-muted">#C1C1C2</small>
</div>
</div>
<div className="d-flex flex-row align-items-center gap-3">
<div style={{ width: '40px', height: '40px', backgroundColor: '#111112', borderRadius: '4px', flexShrink: 0, border: '1px solid var(--bs-border-color, #dee2e6)' }}></div>
<div>
<strong>Base:</strong> <code>$gray-900</code>
<br />
<small className="text-muted">#111112</small>
</div>
</div>
<div className="d-flex flex-row align-items-center gap-3">
<div style={{ width: '40px', height: '40px', backgroundColor: '#21E46B', borderRadius: '4px', flexShrink: 0, border: '1px solid var(--bs-border-color, #dee2e6)' }}></div>
<div>
<strong>Green:</strong> <code>$green-300</code>
<br />
<small className="text-muted">#21E46B</small>
</div>
</div>
</div>
</div>
</div>
<div className="d-flex flex-column gap-5 mb-10">
<div>
<h6 className="mb-3">Gray - Neutral separation (default)</h6>
<Divider color="gray" weight="regular" />
</div>
<div>
<h6 className="mb-3">Base - High contrast separation (adapts to theme)</h6>
<Divider color="base" weight="regular" />
</div>
<div>
<h6 className="mb-3">Green - Brand accent separation</h6>
<Divider color="green" weight="regular" />
</div>
</div>
</PageGridCol>
</PageGridRow>
</PageGrid>
{/* Real-World Examples */}
<PageGrid className="py-26">
<PageGridRow>
<PageGridCol span={12}>
<h2 className="h4 mb-6">Real-World Examples</h2>
<div className="d-flex flex-column gap-6 mb-10">
{/* Content Section Separation */}
<div>
<h6 className="mb-4">Content Section Separation</h6>
<div className="card p-4">
<h5>Section Title</h5>
<p className="mb-4">This is some content in the first section that explains something important.</p>
<Divider color="gray" weight="thin" />
<p className="mt-4 mb-0">This is content in the second section that follows naturally from the first.</p>
</div>
</div>
{/* List Item Separation */}
<div>
<h6 className="mb-4">List Item Separation</h6>
<div className="card p-4">
<div className="d-flex flex-column">
<div className="py-3">
<strong>Feature One</strong>
<p className="mb-0 text-muted">Description of the first feature</p>
</div>
<Divider color="gray" weight="thin" />
<div className="py-3">
<strong>Feature Two</strong>
<p className="mb-0 text-muted">Description of the second feature</p>
</div>
<Divider color="gray" weight="thin" />
<div className="py-3">
<strong>Feature Three</strong>
<p className="mb-0 text-muted">Description of the third feature</p>
</div>
</div>
</div>
</div>
{/* Vertical Divider Between Columns */}
<div>
<h6 className="mb-4">Vertical Divider Between Columns</h6>
<div className="card p-4">
<div className="d-flex flex-row align-items-stretch" style={{ gap: '1.5rem', minHeight: '100px' }}>
<div style={{ flex: '1 1 0' }}>
<strong>Column One</strong>
<p className="mb-0 text-muted">Content for the first column</p>
</div>
<Divider orientation="vertical" color="gray" weight="regular" />
<div style={{ flex: '1 1 0' }}>
<strong>Column Two</strong>
<p className="mb-0 text-muted">Content for the second column</p>
</div>
<Divider orientation="vertical" color="gray" weight="regular" />
<div style={{ flex: '1 1 0' }}>
<strong>Column Three</strong>
<p className="mb-0 text-muted">Content for the third column</p>
</div>
</div>
</div>
</div>
{/* Major Section Break */}
<div>
<h6 className="mb-4">Major Section Break (Strong + Green)</h6>
<div className="card p-4">
<h5>Primary Section</h5>
<p className="mb-4">This section contains the main content of the page.</p>
<Divider color="green" weight="strong" />
<h5 className="mt-4">Secondary Section</h5>
<p className="mb-0">This section is clearly separated with a strong branded divider.</p>
</div>
</div>
{/* Navigation Separator */}
<div>
<h6 className="mb-4">Navigation Item Separator</h6>
<div className="card p-4">
<div className="d-flex flex-row align-items-center" style={{ gap: '1rem', height: '24px' }}>
<span>Home</span>
<Divider orientation="vertical" color="gray" weight="thin" />
<span>Documentation</span>
<Divider orientation="vertical" color="gray" weight="thin" />
<span>API Reference</span>
<Divider orientation="vertical" color="gray" weight="thin" />
<span>Community</span>
</div>
</div>
</div>
</div>
</PageGridCol>
</PageGridRow>
</PageGrid>
{/* API Reference */}
<PageGrid className="py-26">
<PageGridRow>
<PageGridCol span={12}>
<h2 className="h4 mb-6">Component API</h2>
<div className="mb-10">
{/* Header Row */}
<div className="d-flex flex-row mb-3 pb-2" style={{ gap: '1rem', borderBottom: '2px solid var(--bs-border-color, #dee2e6)' }}>
<div style={{ width: '120px', flexShrink: 0 }}><strong>Prop</strong></div>
<div style={{ flex: '1 1 0', minWidth: 0 }}><strong>Type</strong></div>
<div style={{ width: '120px', flexShrink: 0 }}><strong>Default</strong></div>
<div style={{ flex: '1 1 0', minWidth: 0 }}><strong>Description</strong></div>
</div>
{/* orientation */}
<div className="d-flex flex-row py-3" style={{ gap: '1rem' }}>
<div style={{ width: '120px', flexShrink: 0 }}><code>orientation</code></div>
<div style={{ flex: '1 1 0', minWidth: 0 }}><code>'horizontal' | 'vertical'</code></div>
<div style={{ width: '120px', flexShrink: 0 }}><code>'horizontal'</code></div>
<div style={{ flex: '1 1 0', minWidth: 0 }}>Sets the divider orientation</div>
</div>
<Divider weight="thin" color="gray" />
{/* weight */}
<div className="d-flex flex-row py-3" style={{ gap: '1rem' }}>
<div style={{ width: '120px', flexShrink: 0 }}><code>weight</code></div>
<div style={{ flex: '1 1 0', minWidth: 0 }}><code>'thin' | 'regular' | 'strong'</code></div>
<div style={{ width: '120px', flexShrink: 0 }}><code>'regular'</code></div>
<div style={{ flex: '1 1 0', minWidth: 0 }}>Controls the stroke thickness</div>
</div>
<Divider weight="thin" color="gray" />
{/* color */}
<div className="d-flex flex-row py-3" style={{ gap: '1rem' }}>
<div style={{ width: '120px', flexShrink: 0 }}><code>color</code></div>
<div style={{ flex: '1 1 0', minWidth: 0 }}><code>'gray' | 'base' | 'green'</code></div>
<div style={{ width: '120px', flexShrink: 0 }}><code>'gray'</code></div>
<div style={{ flex: '1 1 0', minWidth: 0 }}>Sets the divider color</div>
</div>
<Divider weight="thin" color="gray" />
{/* className */}
<div className="d-flex flex-row py-3" style={{ gap: '1rem' }}>
<div style={{ width: '120px', flexShrink: 0 }}><code>className</code></div>
<div style={{ flex: '1 1 0', minWidth: 0 }}><code>string</code></div>
<div style={{ width: '120px', flexShrink: 0 }}><code>''</code></div>
<div style={{ flex: '1 1 0', minWidth: 0 }}>Additional CSS classes</div>
</div>
<Divider weight="thin" color="gray" />
{/* decorative */}
<div className="d-flex flex-row py-3" style={{ gap: '1rem' }}>
<div style={{ width: '120px', flexShrink: 0 }}><code>decorative</code></div>
<div style={{ flex: '1 1 0', minWidth: 0 }}><code>boolean</code></div>
<div style={{ width: '120px', flexShrink: 0 }}><code>true</code></div>
<div style={{ flex: '1 1 0', minWidth: 0 }}>Whether the divider is purely decorative (hides from screen readers)</div>
</div>
</div>
</PageGridCol>
</PageGridRow>
</PageGrid>
</div>
</div>
);
}

View File

@@ -0,0 +1,350 @@
# Divider Component Documentation
## Overview
The Divider component is a visual separator that creates clear boundaries between content sections, elements, or groups. Following the XRPL Brand Design System (BDS), it supports two orientations, three stroke weights, and three color variants to adapt to different visual contexts and hierarchy needs.
## Features
- **Two Orientations**: Horizontal (default) and Vertical
- **Three Stroke Weights**: Thin (0.5px), Regular (1px), Strong (2px)
- **Three Color Variants**: Gray (default), Base (adapts to theme), Green
- **Theme Support**: Automatic light/dark mode adaptation
- **Accessibility**: Configurable for decorative or semantic use
- **Flexible Sizing**: Inherits width/height from parent container
## Props API
```typescript
interface DividerProps {
/** Divider orientation - horizontal separates vertical content, vertical separates horizontal content */
orientation?: 'horizontal' | 'vertical';
/** Stroke weight - controls visual thickness */
weight?: 'thin' | 'regular' | 'strong';
/** Color variant - gray (default), base for high contrast (adapts to theme), green for brand emphasis */
color?: 'gray' | 'base' | 'green';
/** Additional CSS classes */
className?: string;
/** Whether the divider is purely decorative (hides from screen readers) */
decorative?: boolean;
}
```
### Default Values
- `orientation`: `'horizontal'`
- `weight`: `'regular'`
- `color`: `'gray'`
- `className`: `''`
- `decorative`: `true`
## Orientations
### Horizontal Divider
Horizontal dividers extend left to right to separate vertically stacked content. They span the full width of their container by default.
**Common Uses:**
- Between content blocks or sections
- Separating list items
- Within cards to divide content areas
**Usage:**
```tsx
<Divider orientation="horizontal" />
// or simply (horizontal is default)
<Divider />
```
### Vertical Divider
Vertical dividers extend top to bottom to separate horizontally aligned content. They inherit height from their parent container.
**Common Uses:**
- Between columns in a layout
- Separating navigation items
- Within toolbars or action bars
**Usage:**
```tsx
<Divider orientation="vertical" />
```
**Note:** Vertical dividers require a parent container with a defined height.
## Stroke Weights
### Thin (0.5px)
The lightest weight for subtle, unobtrusive separation. Use when content is closely related but needs minimal visual distinction.
**Best For:**
- Within cards or small content areas
- Between tightly grouped elements
- Dense layouts where heavier dividers would feel cluttered
**Usage:**
```tsx
<Divider weight="thin" />
```
### Regular (1px) - Default
The standard weight for most use cases. Provides clear separation without dominating the visual hierarchy.
**Best For:**
- Most layout and section separations
- Between content blocks
- General-purpose dividers
**Usage:**
```tsx
<Divider weight="regular" />
// or simply (regular is default)
<Divider />
```
### Strong (2px)
The heaviest weight for maximum emphasis. Use sparingly to highlight major transitions or boundaries.
**Best For:**
- Major section breaks
- Key boundaries between distinct content areas
- Emphasizing important transitions
**Usage:**
```tsx
<Divider weight="strong" />
```
## Color Variants
### Gray (Default)
Neutral, subtle separation that works in most contexts without drawing attention.
**Best For:**
- Most separations
- Subtle visual breaks
- Backgrounds where you don't want the divider to stand out
**Usage:**
```tsx
<Divider color="gray" />
// or simply (gray is default)
<Divider />
```
### Base
High-contrast separation that adapts to the theme - renders as white in dark mode and black in light mode.
**Best For:**
- When maximum contrast is needed
- Important structural boundaries
- Universal high-visibility dividers
**Usage:**
```tsx
<Divider color="base" />
```
### Green
Brand-colored separation that reinforces XRPL identity or indicates active/important areas.
**Best For:**
- Highlighting branded sections
- Active or selected states
- Drawing attention to specific content areas
**Usage:**
```tsx
<Divider color="green" />
```
## When to Use
Use a Divider to:
- **Separate content sections** - Create clear boundaries between distinct content groups
- **Organize lists** - Divide list items for better scanability
- **Structure cards** - Separate header, body, and footer areas within cards
- **Define columns** - Visually separate side-by-side content
- **Indicate transitions** - Mark boundaries between zones of information
## When NOT to Use
Avoid using a Divider when:
- **Spacing alone is sufficient** - If whitespace provides enough separation, skip the divider
- **Backgrounds provide contrast** - Different background colors may eliminate the need for dividers
- **It adds clutter** - In minimal designs, too many dividers can distract from content
- **To compensate for poor spacing** - Dividers should enhance, not replace, proper layout structure
## Usage Examples
### Basic Usage
```tsx
import { Divider } from 'shared/components/Divider';
// Default horizontal divider
<Divider />
// Vertical divider
<Divider orientation="vertical" />
// Strong green divider for emphasis
<Divider weight="strong" color="green" />
```
### List Separation
```tsx
<div className="list">
<div className="list-item">Item One</div>
<Divider weight="thin" />
<div className="list-item">Item Two</div>
<Divider weight="thin" />
<div className="list-item">Item Three</div>
</div>
```
### Card Content Separation
```tsx
<div className="card">
<div className="card-header">
<h3>Card Title</h3>
</div>
<Divider weight="thin" color="gray" />
<div className="card-body">
<p>Card content goes here...</p>
</div>
<Divider weight="thin" color="gray" />
<div className="card-footer">
<button>Action</button>
</div>
</div>
```
### Column Separation
```tsx
<div className="d-flex align-items-stretch" style={{ height: '200px' }}>
<div className="column">Column One</div>
<Divider orientation="vertical" color="gray" />
<div className="column">Column Two</div>
<Divider orientation="vertical" color="gray" />
<div className="column">Column Three</div>
</div>
```
### Navigation Separation
```tsx
<nav className="d-flex align-items-center" style={{ height: '24px', gap: '1rem' }}>
<a href="/">Home</a>
<Divider orientation="vertical" weight="thin" />
<a href="/docs">Documentation</a>
<Divider orientation="vertical" weight="thin" />
<a href="/api">API</a>
</nav>
```
### Major Section Break
```tsx
<section>
<h2>Primary Content</h2>
<p>Main content area...</p>
</section>
<Divider weight="strong" color="green" />
<section>
<h2>Secondary Content</h2>
<p>Supporting content area...</p>
</section>
```
### Semantic Divider (Accessible)
```tsx
// For dividers that should be announced by screen readers
<Divider decorative={false} />
```
## Accessibility
### Decorative vs Semantic
By default, dividers are decorative (`decorative={true}`) and hidden from screen readers:
- Sets `aria-hidden="true"`
- Uses `role="presentation"`
For semantic dividers that should be announced:
- Set `decorative={false}`
- Adds `role="separator"`
- Includes `aria-orientation` attribute
### Keyboard Navigation
Dividers are non-interactive elements and do not receive focus.
### Color Contrast
- **Gray variant**: Meets contrast requirements on dark backgrounds; may need weight adjustment on light backgrounds
- **Base variant**: High contrast in both themes (adapts to theme - white in dark, black in light)
- **Green variant**: Brand color provides good contrast in both themes
## Best Practices
1. **Use consistent weights** - Stick to one weight within the same context (e.g., all list dividers should be the same weight)
2. **Match hierarchy to importance** - Use thinner dividers for minor separations, stronger for major breaks
3. **Don't overuse dividers** - If every element has a divider, none stand out; use sparingly for maximum effect
4. **Consider spacing first** - Before adding a divider, try adjusting margins or padding
5. **Maintain alignment** - Dividers should align with content; avoid full-width dividers in padded containers
6. **Use color purposefully** - Reserve green for branded emphasis; gray for most cases; base for high contrast needs
7. **Test in both themes** - Verify dividers are visible and appropriate in both light and dark modes
8. **Parent container setup** - For vertical dividers, ensure the parent has `display: flex`, `align-items: stretch`, and a defined height
## Theme Support
The component automatically adapts colors for light and dark modes:
| Color | Dark Mode (Default) | Light Mode |
|-------|---------------------|------------|
| Gray | `$gray-600` (#454549) | `$gray-300` (#C1C1C2) |
| Base | `$white` (#FFFFFF) | `$gray-900` (#111112) |
| Green | `$green-300` (#21E46B) | `$green-300` (#21E46B) |
## Related Components
- **Card** - Often uses horizontal dividers between sections
- **List** - May use dividers between list items
- **Navigation** - Vertical dividers separate nav groups
- **Form** - Dividers separate form sections
## Showcase
See the interactive showcase at `/about/divider-showcase` for live examples of all variants, weights, colors, and real-world usage patterns.
## File Structure
```
shared/components/Divider/
├── Divider.tsx # Component implementation
├── Divider.scss # Component styles
├── Divider.md # This documentation
└── index.ts # Exports
```

View File

@@ -0,0 +1,173 @@
// BDS Divider Component Styles
// Brand Design System - Visual separator component
//
// Naming Convention: BEM with 'bds' namespace
// .bds-divider - Base divider (removes default hr styling)
// .bds-divider--horizontal - Horizontal orientation (default)
// .bds-divider--vertical - Vertical orientation
// .bds-divider--thin - Thin stroke weight (0.5px)
// .bds-divider--regular - Regular stroke weight (1px, default)
// .bds-divider--strong - Strong stroke weight (2px)
// .bds-divider--gray - Gray color variant (default)
// .bds-divider--base - Base color variant (high contrast, adapts to theme)
// .bds-divider--green - Green color variant
//
// Note: This file is imported within xrpl.scss after Bootstrap and project
// variables are loaded, so $grid-breakpoints, colors, and mixins are available.
// =============================================================================
// Design Tokens
// =============================================================================
// Stroke Weights (from Figma design spec)
$bds-divider-thin: 0.5px;
$bds-divider-regular: 1px;
$bds-divider-strong: 2px;
// Colors - Dark Mode (default, mapped from _colors.scss)
// Site defaults to dark mode, uses html.light for light mode
$bds-divider-gray-dark: $gray-600; // #454549 - visible on dark backgrounds
$bds-divider-base-dark: $white; // #FFFFFF - high contrast for dark mode
$bds-divider-green-dark: $green-300; // #21E46B - brand color stays same
// Colors - Light Mode (mapped from _colors.scss)
// Figma Neutral300 (#CAD4DF) → closest match: $gray-300
// Figma Black (#141414) → closest match: $gray-900
// Figma Green 300 (#21E46B) → exact match: $green-300
$bds-divider-gray-light: $gray-300; // #C1C1C2
$bds-divider-base-light: $gray-900; // #111112 - high contrast for light mode
$bds-divider-green-light: $green-300; // #21E46B
// =============================================================================
// Base Divider Styles
// =============================================================================
// Use more specific selector to override Bootstrap hr styles
// hr.bds-divider has higher specificity than just hr
hr.bds-divider,
.bds-divider {
// Reset default <hr> styles and override Bootstrap/global hr styles
margin: 0;
padding: 0;
border: none;
border-top: none;
border-bottom: none;
border-left: none;
border-right: none;
background: transparent;
opacity: 1; // Override Bootstrap's hr opacity: 0.25
color: transparent; // Override inherited color
// Default to gray color (dark mode default)
background-color: $bds-divider-gray-dark;
}
// =============================================================================
// Orientation Modifiers
// =============================================================================
// Horizontal divider - spans full width, uses height for stroke
.bds-divider--horizontal {
display: block;
width: 100%;
min-width: 100%;
// Height set by weight modifier
}
// Vertical divider - spans full height, uses width for stroke
.bds-divider--vertical {
display: block;
height: auto;
min-height: 1px; // Fallback minimum
align-self: stretch; // Stretch to fill flex container height
flex-shrink: 0; // Prevent collapsing in flex layouts
// Width set by weight modifier
}
// =============================================================================
// Weight Modifiers
// =============================================================================
// Thin weight (0.5px)
.bds-divider--thin {
&.bds-divider--horizontal {
height: $bds-divider-thin;
}
&.bds-divider--vertical {
width: $bds-divider-thin;
}
}
// Regular weight (1px) - default
.bds-divider--regular {
&.bds-divider--horizontal {
height: $bds-divider-regular;
}
&.bds-divider--vertical {
width: $bds-divider-regular;
}
}
// Strong weight (2px)
.bds-divider--strong {
&.bds-divider--horizontal {
height: $bds-divider-strong;
}
&.bds-divider--vertical {
width: $bds-divider-strong;
}
}
// =============================================================================
// Color Modifiers - Dark Mode (Default)
// =============================================================================
// Site defaults to dark mode, so base styles are for dark backgrounds
// Use hr.bds-divider--* for higher specificity to override base hr.bds-divider
// Gray variant (default) - subtle, neutral separation
hr.bds-divider--gray,
.bds-divider--gray {
background-color: $bds-divider-gray-dark;
}
// Base variant - high contrast, adapts to theme (white in dark mode, black in light mode)
hr.bds-divider--base,
.bds-divider--base {
background-color: $bds-divider-base-dark;
}
// Green variant - branded, accent separation
hr.bds-divider--green,
.bds-divider--green {
background-color: $bds-divider-green-dark;
}
// =============================================================================
// Light Mode Styles
// =============================================================================
// Site uses html.light class for light mode
html.light {
// Gray variant in light mode
hr.bds-divider--gray,
.bds-divider--gray {
background-color: $bds-divider-gray-light;
}
// Base variant in light mode - use dark color for contrast
hr.bds-divider--base,
.bds-divider--base {
background-color: $bds-divider-base-light;
}
// Green variant stays the same in light mode (brand color)
hr.bds-divider--green,
.bds-divider--green {
background-color: $bds-divider-green-light;
}
}

View File

@@ -0,0 +1,63 @@
import React from 'react';
export interface DividerProps {
/** Divider orientation - horizontal separates vertical content, vertical separates horizontal content */
orientation?: 'horizontal' | 'vertical';
/** Stroke weight - controls visual thickness */
weight?: 'thin' | 'regular' | 'strong';
/** Color variant - gray (default), base for high contrast (adapts to theme), green for brand emphasis */
color?: 'gray' | 'base' | 'green';
/** Additional CSS classes */
className?: string;
/** Whether the divider is purely decorative (hides from screen readers) */
decorative?: boolean;
}
/**
* BDS Divider Component
*
* A visual separator component following the XRPL Brand Design System.
* Provides clear visual separation between sections, elements, or content groups.
*
* @example
* // Horizontal divider (default)
* <Divider />
*
* // Vertical divider between columns
* <Divider orientation="vertical" />
*
* // Strong green divider for emphasis
* <Divider weight="strong" color="green" />
*
* // Thin base divider (high contrast - adapts to theme)
* <Divider weight="thin" color="base" />
*/
export const Divider: React.FC<DividerProps> = ({
orientation = 'horizontal',
weight = 'regular',
color = 'gray',
className = '',
decorative = true,
}) => {
// Build class names using BEM with bds namespace
const classNames = [
'bds-divider',
`bds-divider--${orientation}`,
`bds-divider--${weight}`,
`bds-divider--${color}`,
className,
]
.filter(Boolean)
.join(' ');
return (
<hr
className={classNames}
aria-hidden={decorative}
role={decorative ? 'presentation' : 'separator'}
aria-orientation={!decorative ? orientation : undefined}
/>
);
};
export default Divider;

View File

@@ -0,0 +1,2 @@
export { Divider, type DividerProps } from './Divider';
export { default } from './Divider';

View File

@@ -15125,6 +15125,84 @@ html:not(.light) .bds-link.bds-link--disabled:visited {
display: none;
}
hr.bds-divider,
.bds-divider {
margin: 0;
padding: 0;
border: none;
border-top: none;
border-bottom: none;
border-left: none;
border-right: none;
background: transparent;
opacity: 1;
color: transparent;
background-color: #454549;
}
.bds-divider--horizontal {
display: block;
width: 100%;
min-width: 100%;
}
.bds-divider--vertical {
display: block;
height: auto;
min-height: 1px;
align-self: stretch;
flex-shrink: 0;
}
.bds-divider--thin.bds-divider--horizontal {
height: 0.5px;
}
.bds-divider--thin.bds-divider--vertical {
width: 0.5px;
}
.bds-divider--regular.bds-divider--horizontal {
height: 1px;
}
.bds-divider--regular.bds-divider--vertical {
width: 1px;
}
.bds-divider--strong.bds-divider--horizontal {
height: 2px;
}
.bds-divider--strong.bds-divider--vertical {
width: 2px;
}
hr.bds-divider--gray,
.bds-divider--gray {
background-color: #454549;
}
hr.bds-divider--base,
.bds-divider--base {
background-color: #FFFFFF;
}
hr.bds-divider--green,
.bds-divider--green {
background-color: #21E46B;
}
html.light hr.bds-divider--gray,
html.light .bds-divider--gray {
background-color: #C1C1C2;
}
html.light hr.bds-divider--base,
html.light .bds-divider--base {
background-color: #111112;
}
html.light hr.bds-divider--green,
html.light .bds-divider--green {
background-color: #21E46B;
}
pre {
color: #FFFFFF;
background-color: #232325;

View File

@@ -90,6 +90,7 @@ $line-height-base: 1.5;
@import "../shared/components/PageGrid/page-grid";
@import "../shared/components/Link/_link-icons.scss";
@import "../shared/components/Link/_link.scss";
@import "../shared/components/Divider/Divider.scss";
@import "_code-tabs.scss";
@import "_code-walkthrough.scss";
@import "_diagrams.scss";