mirror of
https://github.com/XRPLF/xrpl-dev-portal.git
synced 2025-12-06 17:27:57 +00:00
Add CardOffgrid component with showcase and documentation
- Introduced the CardOffgrid component, designed for displaying feature highlights with customizable icons, titles, and descriptions. - Implemented two color variants: neutral and green, with interactive states and a unique hover animation. - Created a comprehensive showcase page demonstrating all variants and states of the CardOffgrid component. - Added detailed documentation covering usage guidelines, best practices, and API reference. - Included SCSS styles for the CardOffgrid component, ensuring compatibility with both light and dark themes.
This commit is contained in:
668
about/card-offgrid-showcase.page.tsx
Normal file
668
about/card-offgrid-showcase.page.tsx
Normal file
@@ -0,0 +1,668 @@
|
||||
import * as React from "react";
|
||||
import { PageGrid, PageGridRow, PageGridCol } from "shared/components/PageGrid/page-grid";
|
||||
import { CardOffgrid } from "shared/components/CardOffgrid";
|
||||
import { Divider } from "shared/components/Divider";
|
||||
|
||||
export const frontmatter = {
|
||||
seo: {
|
||||
title: 'CardOffgrid Component Showcase',
|
||||
description: "A comprehensive showcase of all CardOffgrid component variants, states, and interactions in the XRPL.org Design System.",
|
||||
}
|
||||
};
|
||||
|
||||
// Sample icon component for demonstration
|
||||
const SampleIcon = () => (
|
||||
<svg width="68" height="68" viewBox="0 0 68 68" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M34 8L58 20V44L34 56L10 44V20L34 8Z" stroke="currentColor" strokeWidth="2" fill="none"/>
|
||||
<path d="M34 8V32M34 32L58 20M34 32L10 20" stroke="currentColor" strokeWidth="2"/>
|
||||
<path d="M34 32V56" stroke="currentColor" strokeWidth="2"/>
|
||||
<circle cx="34" cy="32" r="6" fill="currentColor"/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
// Alternative icon for variety
|
||||
const MetadataIcon = () => (
|
||||
<svg width="68" height="68" viewBox="0 0 68 68" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14 18C14 15.7909 15.7909 14 18 14H50C52.2091 14 54 15.7909 54 18V50C54 52.2091 52.2091 54 50 54H18C15.7909 54 14 52.2091 14 50V18Z" stroke="currentColor" strokeWidth="2"/>
|
||||
<path d="M22 26H46M22 34H46M22 42H34" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
// Chain icon
|
||||
const ChainIcon = () => (
|
||||
<svg width="68" height="68" viewBox="0 0 68 68" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M28 34H40M24 28C24 25.7909 25.7909 24 28 24H32C34.2091 24 36 25.7909 36 28V40C36 42.2091 34.2091 44 32 44H28C25.7909 44 24 42.2091 24 40V28Z" stroke="currentColor" strokeWidth="2"/>
|
||||
<path d="M32 28C32 25.7909 33.7909 24 36 24H40C42.2091 24 44 25.7909 44 28V40C44 42.2091 42.2091 44 40 44H36C33.7909 44 32 42.2091 32 40V28Z" stroke="currentColor" strokeWidth="2"/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default function CardOffgridShowcase() {
|
||||
const [clickedCard, setClickedCard] = React.useState<string | null>(null);
|
||||
|
||||
const handleCardClick = (cardName: string) => {
|
||||
setClickedCard(cardName);
|
||||
setTimeout(() => setClickedCard(null), 1500);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="landing">
|
||||
<div className="overflow-hidden">
|
||||
{/* Hero Section */}
|
||||
<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">CardOffgrid Component</h1>
|
||||
<p className="longform">
|
||||
A versatile card component for displaying feature highlights with an icon, title, and description.
|
||||
Supports neutral and green color variants with interactive states and bottom-to-top gradient hover animation.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Variant Showcase */}
|
||||
<PageGrid className="py-26">
|
||||
<PageGridRow>
|
||||
<PageGridCol span={12}>
|
||||
<h2 className="h4 mb-6">Color Variants</h2>
|
||||
<p className="mb-6">CardOffgrid supports two color variants: <strong>neutral</strong> (default) and <strong>green</strong>.</p>
|
||||
|
||||
<div className="d-flex flex-row gap-6 mb-6" style={{ flexWrap: 'wrap' }}>
|
||||
<div>
|
||||
<h6 className="mb-3">Neutral Variant (Default)</h6>
|
||||
<CardOffgrid
|
||||
variant="neutral"
|
||||
icon={<SampleIcon />}
|
||||
title={"Onchain\nMetadata"}
|
||||
description="Easily store key asset information or link to off-chain data using simple APIs, giving token holders transparency."
|
||||
onClick={() => handleCardClick('neutral')}
|
||||
/>
|
||||
{clickedCard === 'neutral' && (
|
||||
<p className="mt-2 text-success">✓ Card clicked!</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h6 className="mb-3">Green Variant</h6>
|
||||
<CardOffgrid
|
||||
variant="green"
|
||||
icon={<SampleIcon />}
|
||||
title={"Onchain\nMetadata"}
|
||||
description="Easily store key asset information or link to off-chain data using simple APIs, giving token holders transparency."
|
||||
onClick={() => handleCardClick('green')}
|
||||
/>
|
||||
{clickedCard === 'green' && (
|
||||
<p className="mt-2 text-success">✓ Card clicked!</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</PageGridCol>
|
||||
</PageGridRow>
|
||||
</PageGrid>
|
||||
|
||||
{/* Interactive States */}
|
||||
<PageGrid className="py-26">
|
||||
<PageGridRow>
|
||||
<PageGridCol span={12}>
|
||||
<h2 className="h4 mb-6">Interactive States</h2>
|
||||
<p className="mb-6">Hover, focus, and press the cards below to see the state transitions.</p>
|
||||
|
||||
{/* Neutral States */}
|
||||
<h5 className="mb-4">Neutral Variant States</h5>
|
||||
<div className="d-flex flex-row gap-4 mb-8" style={{ flexWrap: 'wrap' }}>
|
||||
<div className="text-center">
|
||||
<small className="d-block mb-2 text-muted">Default</small>
|
||||
<CardOffgrid
|
||||
variant="neutral"
|
||||
icon={<MetadataIcon />}
|
||||
title={"Token\nManagement"}
|
||||
description="Create and manage fungible and non-fungible tokens with built-in compliance features."
|
||||
onClick={() => handleCardClick('neutral-default')}
|
||||
/>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<small className="d-block mb-2 text-muted">Disabled</small>
|
||||
<CardOffgrid
|
||||
variant="neutral"
|
||||
icon={<MetadataIcon />}
|
||||
title={"Token\nManagement"}
|
||||
description="Create and manage fungible and non-fungible tokens with built-in compliance features."
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Green States */}
|
||||
<h5 className="mb-4">Green Variant States</h5>
|
||||
<div className="d-flex flex-row gap-4 mb-6" style={{ flexWrap: 'wrap' }}>
|
||||
<div className="text-center">
|
||||
<small className="d-block mb-2 text-muted">Default</small>
|
||||
<CardOffgrid
|
||||
variant="green"
|
||||
icon={<ChainIcon />}
|
||||
title={"Cross-Chain\nBridges"}
|
||||
description="Connect XRPL with other blockchain networks through secure and efficient bridge protocols."
|
||||
onClick={() => handleCardClick('green-default')}
|
||||
/>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<small className="d-block mb-2 text-muted">Disabled</small>
|
||||
<CardOffgrid
|
||||
variant="green"
|
||||
icon={<ChainIcon />}
|
||||
title={"Cross-Chain\nBridges"}
|
||||
description="Connect XRPL with other blockchain networks through secure and efficient bridge protocols."
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</PageGridCol>
|
||||
</PageGridRow>
|
||||
</PageGrid>
|
||||
|
||||
{/* Color Palette */}
|
||||
<PageGrid className="py-26">
|
||||
<PageGridRow>
|
||||
<PageGridCol span={12}>
|
||||
<h2 className="h4 mb-6">Color Palette</h2>
|
||||
<p className="mb-6">
|
||||
All colors are mapped from <code>styles/_colors.scss</code>.
|
||||
The site defaults to <strong>dark mode</strong>. Light mode is activated via <code>html.light</code>.
|
||||
</p>
|
||||
|
||||
{/* Dark Mode Colors */}
|
||||
<h5 className="mb-4">Dark Mode (Default)</h5>
|
||||
<div className="d-flex flex-row gap-6 mb-6" style={{ flexWrap: 'wrap' }}>
|
||||
{/* Neutral Colors - Dark */}
|
||||
<div style={{ flex: '1 1 400px', minWidth: '320px' }}>
|
||||
<h6 className="mb-4">Neutral Variant</h6>
|
||||
<div className="d-flex flex-column gap-3">
|
||||
<div className="d-flex flex-row align-items-center gap-3">
|
||||
<div style={{ width: '60px', height: '40px', backgroundColor: '#72777E', borderRadius: '4px', flexShrink: 0, border: '1px solid #444' }}></div>
|
||||
<div>
|
||||
<strong>Default:</strong> <code>$gray-500</code>
|
||||
<br />
|
||||
<small className="text-muted">#72777E (white text)</small>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex flex-row align-items-center gap-3">
|
||||
<div style={{ width: '60px', height: '40px', backgroundColor: '#8A919A', borderRadius: '4px', flexShrink: 0, border: '1px solid #444' }}></div>
|
||||
<div>
|
||||
<strong>Hover/Focus:</strong> <code>$gray-400</code>
|
||||
<br />
|
||||
<small className="text-muted">#8A919A (white text)</small>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex flex-row align-items-center gap-3">
|
||||
<div style={{ width: '60px', height: '40px', backgroundColor: 'rgba(114, 119, 126, 0.7)', borderRadius: '4px', flexShrink: 0, border: '1px solid #444' }}></div>
|
||||
<div>
|
||||
<strong>Pressed:</strong> <code>rgba($gray-500, 0.7)</code>
|
||||
<br />
|
||||
<small className="text-muted">70% opacity</small>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex flex-row align-items-center gap-3">
|
||||
<div style={{ width: '60px', height: '40px', backgroundColor: '#72777E', borderRadius: '4px', flexShrink: 0, border: '1px solid #444', opacity: 0.3 }}></div>
|
||||
<div>
|
||||
<strong>Disabled:</strong> <code>$gray-500 @ 30%</code>
|
||||
<br />
|
||||
<small className="text-muted">opacity: 0.3</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Green Colors - Dark */}
|
||||
<div style={{ flex: '1 1 400px', minWidth: '320px' }}>
|
||||
<h6 className="mb-4">Green Variant</h6>
|
||||
<div className="d-flex flex-column gap-3">
|
||||
<div className="d-flex flex-row align-items-center gap-3">
|
||||
<div style={{ width: '60px', height: '40px', backgroundColor: '#21E46B', borderRadius: '4px', flexShrink: 0, border: '1px solid #444' }}></div>
|
||||
<div>
|
||||
<strong>Default:</strong> <code>$green-300</code>
|
||||
<br />
|
||||
<small className="text-muted">#21E46B (black text)</small>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex flex-row align-items-center gap-3">
|
||||
<div style={{ width: '60px', height: '40px', backgroundColor: '#70EE97', borderRadius: '4px', flexShrink: 0, border: '1px solid #444' }}></div>
|
||||
<div>
|
||||
<strong>Hover/Focus:</strong> <code>$green-200</code>
|
||||
<br />
|
||||
<small className="text-muted">#70EE97 (black text)</small>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex flex-row align-items-center gap-3">
|
||||
<div style={{ width: '60px', height: '40px', backgroundColor: '#0DAA3E', borderRadius: '4px', flexShrink: 0, border: '1px solid #444' }}></div>
|
||||
<div>
|
||||
<strong>Pressed:</strong> <code>$green-400</code>
|
||||
<br />
|
||||
<small className="text-muted">#0DAA3E (black text)</small>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex flex-row align-items-center gap-3">
|
||||
<div style={{ width: '60px', height: '40px', backgroundColor: '#72777E', borderRadius: '4px', flexShrink: 0, border: '1px solid #444', opacity: 0.3 }}></div>
|
||||
<div>
|
||||
<strong>Disabled:</strong> <code>$gray-500 @ 30%</code>
|
||||
<br />
|
||||
<small className="text-muted">opacity: 0.3 (white text)</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Divider color="gray" className="my-6" />
|
||||
|
||||
{/* Light Mode Colors */}
|
||||
<h5 className="mb-4">Light Mode (<code>html.light</code>)</h5>
|
||||
<div className="d-flex flex-row gap-6 mb-6" style={{ flexWrap: 'wrap' }}>
|
||||
{/* Neutral Colors - Light */}
|
||||
<div style={{ flex: '1 1 400px', minWidth: '320px' }}>
|
||||
<h6 className="mb-4">Neutral Variant</h6>
|
||||
<div className="d-flex flex-column gap-3">
|
||||
<div className="d-flex flex-row align-items-center gap-3">
|
||||
<div style={{ width: '60px', height: '40px', backgroundColor: '#E6EAF0', borderRadius: '4px', flexShrink: 0, border: '1px solid #ccc' }}></div>
|
||||
<div>
|
||||
<strong>Default:</strong> <code>$gray-200</code>
|
||||
<br />
|
||||
<small className="text-muted">#E6EAF0 (dark text)</small>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex flex-row align-items-center gap-3">
|
||||
<div style={{ width: '60px', height: '40px', backgroundColor: '#CAD4DF', borderRadius: '4px', flexShrink: 0, border: '1px solid #ccc' }}></div>
|
||||
<div>
|
||||
<strong>Hover/Focus:</strong> <code>$gray-300</code>
|
||||
<br />
|
||||
<small className="text-muted">#CAD4DF (black text)</small>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex flex-row align-items-center gap-3">
|
||||
<div style={{ width: '60px', height: '40px', backgroundColor: '#8A919A', borderRadius: '4px', flexShrink: 0, border: '1px solid #ccc' }}></div>
|
||||
<div>
|
||||
<strong>Pressed:</strong> <code>$gray-400</code>
|
||||
<br />
|
||||
<small className="text-muted">#8A919A (black text)</small>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex flex-row align-items-center gap-3">
|
||||
<div style={{ width: '60px', height: '40px', backgroundColor: '#F0F3F7', borderRadius: '4px', flexShrink: 0, border: '1px solid #ccc' }}></div>
|
||||
<div>
|
||||
<strong>Disabled:</strong> <code>$gray-100</code>
|
||||
<br />
|
||||
<small className="text-muted">#F0F3F7 (gray text)</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Green Colors - Light */}
|
||||
<div style={{ flex: '1 1 400px', minWidth: '320px' }}>
|
||||
<h6 className="mb-4">Green Variant</h6>
|
||||
<div className="d-flex flex-column gap-3">
|
||||
<div className="d-flex flex-row align-items-center gap-3">
|
||||
<div style={{ width: '60px', height: '40px', backgroundColor: '#70EE97', borderRadius: '4px', flexShrink: 0, border: '1px solid #ccc' }}></div>
|
||||
<div>
|
||||
<strong>Default:</strong> <code>$green-200</code>
|
||||
<br />
|
||||
<small className="text-muted">#70EE97 (black text)</small>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex flex-row align-items-center gap-3">
|
||||
<div style={{ width: '60px', height: '40px', backgroundColor: '#21E46B', borderRadius: '4px', flexShrink: 0, border: '1px solid #ccc' }}></div>
|
||||
<div>
|
||||
<strong>Hover/Focus:</strong> <code>$green-300</code>
|
||||
<br />
|
||||
<small className="text-muted">#21E46B (black text)</small>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex flex-row align-items-center gap-3">
|
||||
<div style={{ width: '60px', height: '40px', backgroundColor: '#0DAA3E', borderRadius: '4px', flexShrink: 0, border: '1px solid #ccc' }}></div>
|
||||
<div>
|
||||
<strong>Pressed:</strong> <code>$green-400</code>
|
||||
<br />
|
||||
<small className="text-muted">#0DAA3E (black text)</small>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex flex-row align-items-center gap-3">
|
||||
<div style={{ width: '60px', height: '40px', backgroundColor: '#F0F3F7', borderRadius: '4px', flexShrink: 0, border: '1px solid #ccc' }}></div>
|
||||
<div>
|
||||
<strong>Disabled:</strong> <code>$gray-100</code>
|
||||
<br />
|
||||
<small className="text-muted">#F0F3F7 (gray text)</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PageGridCol>
|
||||
</PageGridRow>
|
||||
</PageGrid>
|
||||
|
||||
{/* Animation Details */}
|
||||
<PageGrid className="py-26">
|
||||
<PageGridRow>
|
||||
<PageGridCol span={12}>
|
||||
<h2 className="h4 mb-6">Animation Specifications</h2>
|
||||
|
||||
<div className="d-flex flex-row gap-6 mb-6" style={{ flexWrap: 'wrap' }}>
|
||||
<div style={{ flex: '1 1 300px' }}>
|
||||
<h6 className="mb-3">Timing</h6>
|
||||
<ul className="mb-0">
|
||||
<li><strong>Duration:</strong> 200ms</li>
|
||||
<li><strong>Easing:</strong> <code>cubic-bezier(0.98, 0.12, 0.12, 0.98)</code></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div style={{ flex: '1 1 300px' }}>
|
||||
<h6 className="mb-3">Hover Effect ("Window Shade")</h6>
|
||||
<ul className="mb-0">
|
||||
<li><strong>Hover in:</strong> Shade rises up (bottom → top)</li>
|
||||
<li><strong>Hover out:</strong> Shade falls down (top → bottom)</li>
|
||||
<li>Darker pressed state on click</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div style={{ flex: '1 1 300px' }}>
|
||||
<h6 className="mb-3">State Flow</h6>
|
||||
<ul className="mb-0">
|
||||
<li>Default → Hover → Pressed</li>
|
||||
<li>Full card area is clickable</li>
|
||||
<li>Focus ring on keyboard navigation</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</PageGridCol>
|
||||
</PageGridRow>
|
||||
</PageGrid>
|
||||
|
||||
{/* Link vs Button */}
|
||||
<PageGrid className="py-26">
|
||||
<PageGridRow>
|
||||
<PageGridCol span={12}>
|
||||
<h2 className="h4 mb-6">Link vs Button Rendering</h2>
|
||||
<p className="mb-6">The component renders as an <code><a></code> tag when <code>href</code> is provided, otherwise as a <code><button></code>.</p>
|
||||
|
||||
<div className="d-flex flex-row gap-6 mb-6" style={{ flexWrap: 'wrap' }}>
|
||||
<div>
|
||||
<h6 className="mb-3">As Button (onClick)</h6>
|
||||
<CardOffgrid
|
||||
variant="neutral"
|
||||
icon={<SampleIcon />}
|
||||
title={"Click Me"}
|
||||
description="This card renders as a button element and triggers an onClick handler."
|
||||
onClick={() => handleCardClick('button-demo')}
|
||||
/>
|
||||
{clickedCard === 'button-demo' && (
|
||||
<p className="mt-2 text-success">✓ Button clicked!</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h6 className="mb-3">As Link (href)</h6>
|
||||
<CardOffgrid
|
||||
variant="green"
|
||||
icon={<SampleIcon />}
|
||||
title={"Navigate"}
|
||||
description="This card renders as an anchor element and navigates to the specified href."
|
||||
href="#link-demo"
|
||||
/>
|
||||
<p className="mt-2 text-muted">↑ Click to navigate to #link-demo</p>
|
||||
</div>
|
||||
</div>
|
||||
</PageGridCol>
|
||||
</PageGridRow>
|
||||
</PageGrid>
|
||||
|
||||
{/* Dimensions */}
|
||||
<PageGrid className="py-26">
|
||||
<PageGridRow>
|
||||
<PageGridCol span={12}>
|
||||
<h2 className="h4 mb-6">Dimensions</h2>
|
||||
|
||||
<div className="mb-6">
|
||||
{/* 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: '180px', flexShrink: 0 }}><strong>Property</strong></div>
|
||||
<div style={{ flex: '1 1 0', minWidth: 0 }}><strong>Value</strong></div>
|
||||
</div>
|
||||
|
||||
<div className="d-flex flex-row py-3" style={{ gap: '1rem' }}>
|
||||
<div style={{ width: '180px', flexShrink: 0 }}>Card Width</div>
|
||||
<div style={{ flex: '1 1 0', minWidth: 0 }}><code>400px</code> (full-width on mobile)</div>
|
||||
</div>
|
||||
<Divider weight="thin" color="gray" />
|
||||
|
||||
<div className="d-flex flex-row py-3" style={{ gap: '1rem' }}>
|
||||
<div style={{ width: '180px', flexShrink: 0 }}>Card Height</div>
|
||||
<div style={{ flex: '1 1 0', minWidth: 0 }}><code>480px</code></div>
|
||||
</div>
|
||||
<Divider weight="thin" color="gray" />
|
||||
|
||||
<div className="d-flex flex-row py-3" style={{ gap: '1rem' }}>
|
||||
<div style={{ width: '180px', flexShrink: 0 }}>Padding</div>
|
||||
<div style={{ flex: '1 1 0', minWidth: 0 }}><code>24px</code></div>
|
||||
</div>
|
||||
<Divider weight="thin" color="gray" />
|
||||
|
||||
<div className="d-flex flex-row py-3" style={{ gap: '1rem' }}>
|
||||
<div style={{ width: '180px', flexShrink: 0 }}>Icon Container</div>
|
||||
<div style={{ flex: '1 1 0', minWidth: 0 }}><code>84px × 84px</code></div>
|
||||
</div>
|
||||
<Divider weight="thin" color="gray" />
|
||||
|
||||
<div className="d-flex flex-row py-3" style={{ gap: '1rem' }}>
|
||||
<div style={{ width: '180px', flexShrink: 0 }}>Icon Size</div>
|
||||
<div style={{ flex: '1 1 0', minWidth: 0 }}><code>~68px × 68px</code></div>
|
||||
</div>
|
||||
<Divider weight="thin" color="gray" />
|
||||
|
||||
<div className="d-flex flex-row py-3" style={{ gap: '1rem' }}>
|
||||
<div style={{ width: '180px', flexShrink: 0 }}>Content Gap</div>
|
||||
<div style={{ flex: '1 1 0', minWidth: 0 }}><code>40px</code> (between title and description)</div>
|
||||
</div>
|
||||
</div>
|
||||
</PageGridCol>
|
||||
</PageGridRow>
|
||||
</PageGrid>
|
||||
|
||||
{/* Typography */}
|
||||
<PageGrid className="py-26">
|
||||
<PageGridRow>
|
||||
<PageGridCol span={12}>
|
||||
<h2 className="h4 mb-6">Typography</h2>
|
||||
|
||||
<div className="d-flex flex-row gap-6 mb-6" style={{ flexWrap: 'wrap' }}>
|
||||
<div style={{ flex: '1 1 300px' }}>
|
||||
<h6 className="mb-3">Title</h6>
|
||||
<ul className="mb-0">
|
||||
<li><strong>Font Size:</strong> 32px</li>
|
||||
<li><strong>Font Weight:</strong> 300 (light)</li>
|
||||
<li><strong>Line Height:</strong> 40px</li>
|
||||
<li><strong>Letter Spacing:</strong> -1px</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div style={{ flex: '1 1 300px' }}>
|
||||
<h6 className="mb-3">Description</h6>
|
||||
<ul className="mb-0">
|
||||
<li><strong>Font Size:</strong> 18px</li>
|
||||
<li><strong>Font Weight:</strong> 300 (light)</li>
|
||||
<li><strong>Line Height:</strong> 26.1px</li>
|
||||
<li><strong>Letter Spacing:</strong> -0.5px</li>
|
||||
</ul>
|
||||
</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: '100px', flexShrink: 0 }}><strong>Default</strong></div>
|
||||
<div style={{ flex: '1 1 0', minWidth: 0 }}><strong>Description</strong></div>
|
||||
</div>
|
||||
|
||||
{/* variant */}
|
||||
<div className="d-flex flex-row py-3" style={{ gap: '1rem' }}>
|
||||
<div style={{ width: '120px', flexShrink: 0 }}><code>variant</code></div>
|
||||
<div style={{ flex: '1 1 0', minWidth: 0 }}><code>'neutral' | 'green'</code></div>
|
||||
<div style={{ width: '100px', flexShrink: 0 }}><code>'neutral'</code></div>
|
||||
<div style={{ flex: '1 1 0', minWidth: 0 }}>Color variant of the card</div>
|
||||
</div>
|
||||
<Divider weight="thin" color="gray" />
|
||||
|
||||
{/* icon */}
|
||||
<div className="d-flex flex-row py-3" style={{ gap: '1rem' }}>
|
||||
<div style={{ width: '120px', flexShrink: 0 }}><code>icon</code></div>
|
||||
<div style={{ flex: '1 1 0', minWidth: 0 }}><code>ReactNode | string</code></div>
|
||||
<div style={{ width: '100px', flexShrink: 0 }}>required</div>
|
||||
<div style={{ flex: '1 1 0', minWidth: 0 }}>Icon element or image URL</div>
|
||||
</div>
|
||||
<Divider weight="thin" color="gray" />
|
||||
|
||||
{/* title */}
|
||||
<div className="d-flex flex-row py-3" style={{ gap: '1rem' }}>
|
||||
<div style={{ width: '120px', flexShrink: 0 }}><code>title</code></div>
|
||||
<div style={{ flex: '1 1 0', minWidth: 0 }}><code>string</code></div>
|
||||
<div style={{ width: '100px', flexShrink: 0 }}>required</div>
|
||||
<div style={{ flex: '1 1 0', minWidth: 0 }}>Card title (use \n for line breaks)</div>
|
||||
</div>
|
||||
<Divider weight="thin" color="gray" />
|
||||
|
||||
{/* description */}
|
||||
<div className="d-flex flex-row py-3" style={{ gap: '1rem' }}>
|
||||
<div style={{ width: '120px', flexShrink: 0 }}><code>description</code></div>
|
||||
<div style={{ flex: '1 1 0', minWidth: 0 }}><code>string</code></div>
|
||||
<div style={{ width: '100px', flexShrink: 0 }}>required</div>
|
||||
<div style={{ flex: '1 1 0', minWidth: 0 }}>Card description text</div>
|
||||
</div>
|
||||
<Divider weight="thin" color="gray" />
|
||||
|
||||
{/* onClick */}
|
||||
<div className="d-flex flex-row py-3" style={{ gap: '1rem' }}>
|
||||
<div style={{ width: '120px', flexShrink: 0 }}><code>onClick</code></div>
|
||||
<div style={{ flex: '1 1 0', minWidth: 0 }}><code>() => void</code></div>
|
||||
<div style={{ width: '100px', flexShrink: 0 }}><code>undefined</code></div>
|
||||
<div style={{ flex: '1 1 0', minWidth: 0 }}>Click handler (renders as button)</div>
|
||||
</div>
|
||||
<Divider weight="thin" color="gray" />
|
||||
|
||||
{/* href */}
|
||||
<div className="d-flex flex-row py-3" style={{ gap: '1rem' }}>
|
||||
<div style={{ width: '120px', flexShrink: 0 }}><code>href</code></div>
|
||||
<div style={{ flex: '1 1 0', minWidth: 0 }}><code>string</code></div>
|
||||
<div style={{ width: '100px', flexShrink: 0 }}><code>undefined</code></div>
|
||||
<div style={{ flex: '1 1 0', minWidth: 0 }}>Link destination (renders as anchor)</div>
|
||||
</div>
|
||||
<Divider weight="thin" color="gray" />
|
||||
|
||||
{/* disabled */}
|
||||
<div className="d-flex flex-row py-3" style={{ gap: '1rem' }}>
|
||||
<div style={{ width: '120px', flexShrink: 0 }}><code>disabled</code></div>
|
||||
<div style={{ flex: '1 1 0', minWidth: 0 }}><code>boolean</code></div>
|
||||
<div style={{ width: '100px', flexShrink: 0 }}><code>false</code></div>
|
||||
<div style={{ flex: '1 1 0', minWidth: 0 }}>Disabled state</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: '100px', flexShrink: 0 }}><code>''</code></div>
|
||||
<div style={{ flex: '1 1 0', minWidth: 0 }}>Additional CSS classes</div>
|
||||
</div>
|
||||
</div>
|
||||
</PageGridCol>
|
||||
</PageGridRow>
|
||||
</PageGrid>
|
||||
|
||||
{/* Usage Examples */}
|
||||
<PageGrid className="py-26">
|
||||
<PageGridRow>
|
||||
<PageGridCol span={12}>
|
||||
<h2 className="h4 mb-6">Usage Examples</h2>
|
||||
|
||||
<div className="d-flex flex-column gap-6">
|
||||
{/* Basic Usage */}
|
||||
<div className="card p-4">
|
||||
<h6 className="mb-3">Basic Usage</h6>
|
||||
<pre className="mb-0" style={{ backgroundColor: 'var(--bs-gray-800)', padding: '1rem', borderRadius: '4px', overflow: 'auto' }}>
|
||||
{`import { CardOffgrid } from 'shared/components/CardOffgrid';
|
||||
|
||||
<CardOffgrid
|
||||
variant="neutral"
|
||||
icon={<MyIcon />}
|
||||
title="Onchain\\nMetadata"
|
||||
description="Easily store key asset information..."
|
||||
onClick={() => console.log('clicked')}
|
||||
/>`}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
{/* With Link */}
|
||||
<div className="card p-4">
|
||||
<h6 className="mb-3">With Link</h6>
|
||||
<pre className="mb-0" style={{ backgroundColor: 'var(--bs-gray-800)', padding: '1rem', borderRadius: '4px', overflow: 'auto' }}>
|
||||
{`<CardOffgrid
|
||||
variant="green"
|
||||
icon="/icons/metadata.svg"
|
||||
title="Learn More"
|
||||
description="Click to navigate to documentation..."
|
||||
href="/docs/metadata"
|
||||
/>`}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
{/* Disabled State */}
|
||||
<div className="card p-4">
|
||||
<h6 className="mb-3">Disabled State</h6>
|
||||
<pre className="mb-0" style={{ backgroundColor: 'var(--bs-gray-800)', padding: '1rem', borderRadius: '4px', overflow: 'auto' }}>
|
||||
{`<CardOffgrid
|
||||
variant="neutral"
|
||||
icon={<MyIcon />}
|
||||
title="Coming Soon"
|
||||
description="This feature is not yet available..."
|
||||
disabled
|
||||
/>`}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</PageGridCol>
|
||||
</PageGridRow>
|
||||
</PageGrid>
|
||||
|
||||
{/* Figma References */}
|
||||
<PageGrid className="py-26" id="link-demo">
|
||||
<PageGridRow>
|
||||
<PageGridCol span={12}>
|
||||
<h2 className="h4 mb-6">Figma References</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://www.figma.com/design/vwDwMJ3mFrAklj5zvZwX5M/Card---OffGrid?node-id=8001-1963&m=dev" target="_blank" rel="noopener noreferrer">
|
||||
Light Mode Color States
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://www.figma.com/design/vwDwMJ3mFrAklj5zvZwX5M/Card---OffGrid?node-id=8001-2321&m=dev" target="_blank" rel="noopener noreferrer">
|
||||
Dark Mode Color States
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://www.figma.com/design/vwDwMJ3mFrAklj5zvZwX5M/Card---OffGrid?node-id=8007-1096&m=dev" target="_blank" rel="noopener noreferrer">
|
||||
Animation Specifications
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</PageGridCol>
|
||||
</PageGridRow>
|
||||
</PageGrid>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
436
shared/components/CardOffgrid/CardOffgrid.md
Normal file
436
shared/components/CardOffgrid/CardOffgrid.md
Normal file
@@ -0,0 +1,436 @@
|
||||
# CardOffgrid Component - Usage Guide
|
||||
|
||||
## Overview
|
||||
|
||||
`CardOffgrid` is a feature highlight card component designed to showcase key capabilities, features, or resources. It combines an icon, title, and description in a visually engaging, interactive card format with smooth hover animations.
|
||||
|
||||
**Use CardOffgrid when:**
|
||||
- Highlighting key features or capabilities
|
||||
- Creating feature grids or showcases
|
||||
- Linking to important documentation or resources
|
||||
- Presenting product/service highlights
|
||||
|
||||
**Don't use CardOffgrid for:**
|
||||
- Simple content cards (use standard Bootstrap cards)
|
||||
- Navigation items (use navigation components)
|
||||
- Data display (use tables or data components)
|
||||
- Long-form content (use article/page layouts)
|
||||
|
||||
---
|
||||
|
||||
## When to Use Each Variant
|
||||
|
||||
### Neutral Variant (`variant="neutral"`)
|
||||
|
||||
**Use for:**
|
||||
- General feature highlights
|
||||
- Standard content cards
|
||||
- Secondary or supporting features
|
||||
- When you want subtle, professional presentation
|
||||
|
||||
**Example use cases:**
|
||||
- Documentation sections
|
||||
- Feature lists
|
||||
- Service offerings
|
||||
- Standard informational cards
|
||||
|
||||
### Green Variant (`variant="green"`)
|
||||
|
||||
**Use for:**
|
||||
- Primary or featured highlights
|
||||
- Call-to-action cards
|
||||
- Important announcements
|
||||
- Brand-emphasized content
|
||||
|
||||
**Example use cases:**
|
||||
- Hero feature cards
|
||||
- Primary CTAs
|
||||
- Featured resources
|
||||
- Branded highlights
|
||||
|
||||
---
|
||||
|
||||
## Content Best Practices
|
||||
|
||||
### Title Guidelines
|
||||
|
||||
✅ **Do:**
|
||||
- Keep titles concise (1-3 words ideal)
|
||||
- Use line breaks (`\n`) for multi-word titles when needed
|
||||
- Make titles action-oriented or descriptive
|
||||
- Examples: "Onchain Metadata", "Token\nManagement", "Cross-Chain\nBridges"
|
||||
|
||||
❌ **Don't:**
|
||||
- Write long sentences as titles
|
||||
- Use more than 2 lines
|
||||
- Include punctuation (periods, commas)
|
||||
- Make titles too generic ("Feature", "Service")
|
||||
|
||||
### Description Guidelines
|
||||
|
||||
✅ **Do:**
|
||||
- Write 1-2 sentences (15-25 words ideal)
|
||||
- Focus on benefits or key information
|
||||
- Use clear, simple language
|
||||
- Keep descriptions scannable
|
||||
|
||||
❌ **Don't:**
|
||||
- Write paragraphs (save for full pages)
|
||||
- Use jargon without context
|
||||
- Include multiple ideas in one description
|
||||
- Make descriptions too short (< 10 words) or too long (> 40 words)
|
||||
|
||||
### Icon Guidelines
|
||||
|
||||
✅ **Do:**
|
||||
- Use SVG icons for crisp rendering
|
||||
- Choose icons that represent the feature clearly
|
||||
- Ensure icons are recognizable at 68×68px
|
||||
- Use consistent icon style across cards
|
||||
|
||||
❌ **Don't:**
|
||||
- Use low-resolution raster images
|
||||
- Choose overly complex icons
|
||||
- Mix icon styles within a single grid
|
||||
- Use icons that don't relate to the content
|
||||
|
||||
---
|
||||
|
||||
## Interaction Patterns
|
||||
|
||||
### Using `onClick` vs `href`
|
||||
|
||||
**Use `onClick` when:**
|
||||
- Triggering JavaScript actions (modals, analytics, state changes)
|
||||
- Opening external links in new tabs
|
||||
- Performing client-side navigation
|
||||
- Handling complex interactions
|
||||
|
||||
```tsx
|
||||
<CardOffgrid
|
||||
variant="neutral"
|
||||
icon={<AnalyticsIcon />}
|
||||
title="View Analytics"
|
||||
description="See detailed usage statistics and insights."
|
||||
onClick={() => {
|
||||
trackEvent('analytics_viewed');
|
||||
openModal('analytics');
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
**Use `href` when:**
|
||||
- Navigating to internal pages
|
||||
- Linking to documentation
|
||||
- Simple page navigation
|
||||
- SEO-friendly links
|
||||
|
||||
```tsx
|
||||
<CardOffgrid
|
||||
variant="green"
|
||||
icon="/icons/docs.svg"
|
||||
title="API\nReference"
|
||||
description="Complete API documentation and examples."
|
||||
href="/docs/api"
|
||||
/>
|
||||
```
|
||||
|
||||
### Disabled State
|
||||
|
||||
Use `disabled` when:
|
||||
- Feature is coming soon
|
||||
- Feature requires authentication
|
||||
- Feature is temporarily unavailable
|
||||
- You want to show but not allow interaction
|
||||
|
||||
```tsx
|
||||
<CardOffgrid
|
||||
variant="neutral"
|
||||
icon={<BetaIcon />}
|
||||
title="Coming\nSoon"
|
||||
description="This feature will be available in the next release."
|
||||
disabled
|
||||
/>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Layout Best Practices
|
||||
|
||||
### Grid Layouts
|
||||
|
||||
**Recommended grid patterns:**
|
||||
|
||||
```tsx
|
||||
// 2-column grid (desktop)
|
||||
<div className="row">
|
||||
<div className="col-md-6 mb-4">
|
||||
<CardOffgrid {...props1} />
|
||||
</div>
|
||||
<div className="col-md-6 mb-4">
|
||||
<CardOffgrid {...props2} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
// 3-column grid (desktop)
|
||||
<div className="row">
|
||||
{cards.map(card => (
|
||||
<div key={card.id} className="col-md-4 mb-4">
|
||||
<CardOffgrid {...card} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
```
|
||||
|
||||
**Spacing:**
|
||||
- Use Bootstrap spacing utilities (`mb-4`, `mb-5`) between cards
|
||||
- Maintain consistent spacing in grids
|
||||
- Cards are responsive and stack on mobile automatically
|
||||
|
||||
### Single Card Usage
|
||||
|
||||
For hero sections or featured highlights:
|
||||
|
||||
```tsx
|
||||
<div className="d-flex justify-content-center">
|
||||
<CardOffgrid
|
||||
variant="green"
|
||||
icon={<FeaturedIcon />}
|
||||
title="New Feature"
|
||||
description="Introducing our latest capability..."
|
||||
href="/features/new"
|
||||
/>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Accessibility Best Practices
|
||||
|
||||
### Semantic HTML
|
||||
|
||||
The component automatically renders as:
|
||||
- `<button>` when using `onClick`
|
||||
- `<a>` when using `href`
|
||||
|
||||
This ensures proper semantic meaning for screen readers.
|
||||
|
||||
### Keyboard Navigation
|
||||
|
||||
✅ **Always test:**
|
||||
- Tab navigation moves focus to cards
|
||||
- Enter/Space activates cards
|
||||
- Focus ring is clearly visible
|
||||
- Focus order follows logical reading order
|
||||
|
||||
### Screen Reader Content
|
||||
|
||||
✅ **Ensure:**
|
||||
- Titles are descriptive and unique
|
||||
- Descriptions provide context
|
||||
- Icons have appropriate `aria-hidden="true"` (handled automatically)
|
||||
- Disabled cards communicate their state
|
||||
|
||||
### Color Contrast
|
||||
|
||||
All variants meet WCAG AA standards:
|
||||
- Dark mode: White text on colored backgrounds
|
||||
- Light mode: Dark text on light backgrounds
|
||||
- Focus rings provide sufficient contrast
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Feature Showcase Grid
|
||||
|
||||
```tsx
|
||||
const features = [
|
||||
{
|
||||
variant: 'green',
|
||||
icon: <TokenIcon />,
|
||||
title: 'Token\nManagement',
|
||||
description: 'Create and manage fungible and non-fungible tokens.',
|
||||
href: '/docs/tokens'
|
||||
},
|
||||
{
|
||||
variant: 'neutral',
|
||||
icon: <MetadataIcon />,
|
||||
title: 'Onchain\nMetadata',
|
||||
description: 'Store key asset information using simple APIs.',
|
||||
href: '/docs/metadata'
|
||||
},
|
||||
// ... more features
|
||||
];
|
||||
|
||||
<div className="row">
|
||||
{features.map((feature, index) => (
|
||||
<div key={index} className="col-md-4 mb-4">
|
||||
<CardOffgrid {...feature} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
```
|
||||
|
||||
### Mixed Variants for Hierarchy
|
||||
|
||||
Use green variant for primary features, neutral for supporting:
|
||||
|
||||
```tsx
|
||||
<div className="row">
|
||||
<div className="col-md-6 mb-4">
|
||||
<CardOffgrid
|
||||
variant="green" // Primary feature
|
||||
icon={<PrimaryIcon />}
|
||||
title="Main Feature"
|
||||
description="Our flagship capability..."
|
||||
href="/feature/main"
|
||||
/>
|
||||
</div>
|
||||
<div className="col-md-6 mb-4">
|
||||
<CardOffgrid
|
||||
variant="neutral" // Supporting feature
|
||||
icon={<SupportIcon />}
|
||||
title="Supporting Feature"
|
||||
description="Complementary capability..."
|
||||
href="/feature/support"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Coming Soon Pattern
|
||||
|
||||
```tsx
|
||||
<CardOffgrid
|
||||
variant="neutral"
|
||||
icon={<ComingSoonIcon />}
|
||||
title="Coming\nSoon"
|
||||
description="This feature is currently in development and will be available soon."
|
||||
disabled
|
||||
/>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Icon Optimization
|
||||
|
||||
✅ **Best practices:**
|
||||
- Use SVG React components (inlined) for small icons
|
||||
- Use optimized SVG files for image icons
|
||||
- Avoid large raster images
|
||||
- Consider lazy loading for below-the-fold cards
|
||||
|
||||
### Rendering Performance
|
||||
|
||||
- Cards are lightweight components
|
||||
- Hover animations use CSS transforms (GPU-accelerated)
|
||||
- No heavy JavaScript calculations
|
||||
- Suitable for grids with 10+ cards
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**Card not clickable:**
|
||||
- Ensure `onClick` or `href` is provided
|
||||
- Check that `disabled` is not set to `true`
|
||||
- Verify no parent element is blocking pointer events
|
||||
|
||||
**Icon not displaying:**
|
||||
- Verify icon path is correct (if using string)
|
||||
- Check icon component is properly imported
|
||||
- Ensure icon fits within 68×68px bounds
|
||||
|
||||
**Hover animation not working:**
|
||||
- Check browser supports CSS `clip-path`
|
||||
- Verify no conflicting CSS is overriding transitions
|
||||
- Test in different browsers
|
||||
|
||||
**Focus ring not visible:**
|
||||
- Ensure keyboard navigation (Tab key)
|
||||
- Check focus ring color contrasts with background
|
||||
- Verify `outline-offset: 2px` is applied
|
||||
|
||||
---
|
||||
|
||||
## Design System Integration
|
||||
|
||||
### Color Tokens
|
||||
|
||||
All colors reference `styles/_colors.scss`:
|
||||
- Dark mode (default): Uses `$gray-500`, `$gray-400`, `$green-300`, `$green-200`
|
||||
- Light mode (`html.light`): Uses `$gray-200`, `$gray-300`, `$green-200`, `$green-300`
|
||||
|
||||
### Typography
|
||||
|
||||
- Title: Booton Light, 32px, -1px letter-spacing
|
||||
- Description: Booton Light, 18px, -0.5px letter-spacing
|
||||
|
||||
### Spacing
|
||||
|
||||
- Card padding: 24px
|
||||
- Content gap: 40px (between title and description)
|
||||
- Icon container: 84×84px
|
||||
|
||||
---
|
||||
|
||||
## Figma References
|
||||
|
||||
- **Light Mode Colors**: [Figma Design - Light Mode](https://www.figma.com/design/vwDwMJ3mFrAklj5zvZwX5M/Card---OffGrid?node-id=8001-1963&m=dev)
|
||||
- **Dark Mode Colors**: [Figma Design - Dark Mode](https://www.figma.com/design/vwDwMJ3mFrAklj5zvZwX5M/Card---OffGrid?node-id=8001-2321&m=dev)
|
||||
- **Animation Specs**: [Figma Design - Storyboard](https://www.figma.com/design/vwDwMJ3mFrAklj5zvZwX5M/Card---OffGrid?node-id=8007-1096&m=dev)
|
||||
|
||||
---
|
||||
|
||||
## Component API
|
||||
|
||||
```typescript
|
||||
interface CardOffgridProps {
|
||||
/** Color variant: 'neutral' (default) or 'green' */
|
||||
variant?: 'neutral' | 'green';
|
||||
|
||||
/** Icon element (ReactNode) or image path (string) */
|
||||
icon: React.ReactNode | string;
|
||||
|
||||
/** Card title - use \n for line breaks */
|
||||
title: string;
|
||||
|
||||
/** Card description text (1-2 sentences) */
|
||||
description: string;
|
||||
|
||||
/** Click handler - renders as <button> */
|
||||
onClick?: () => void;
|
||||
|
||||
/** Link destination - renders as <a> */
|
||||
href?: string;
|
||||
|
||||
/** Disabled state - prevents interaction */
|
||||
disabled?: boolean;
|
||||
|
||||
/** Additional CSS classes */
|
||||
className?: string;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Use Case | Variant | Interaction |
|
||||
|----------|---------|-------------|
|
||||
| Standard feature | `neutral` | `href` or `onClick` |
|
||||
| Primary feature | `green` | `href` or `onClick` |
|
||||
| Coming soon | `neutral` | `disabled` |
|
||||
| Feature grid | Mix both | `href` preferred |
|
||||
| Hero section | `green` | `href` |
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
See the [CardOffgrid Showcase](/about/card-offgrid-showcase) for live examples and interactive demos.
|
||||
376
shared/components/CardOffgrid/CardOffgrid.scss
Normal file
376
shared/components/CardOffgrid/CardOffgrid.scss
Normal file
@@ -0,0 +1,376 @@
|
||||
// BDS CardOffgrid Component Styles
|
||||
// Brand Design System - Feature card with icon, title, and description
|
||||
//
|
||||
// Naming Convention: BEM with 'bds' namespace
|
||||
// .bds-card-offgrid - Base card (resets button/anchor styles)
|
||||
// .bds-card-offgrid--neutral - Neutral gray color variant (default)
|
||||
// .bds-card-offgrid--green - Green color variant
|
||||
// .bds-card-offgrid--disabled - Disabled state
|
||||
// .bds-card-offgrid__overlay - Hover gradient overlay
|
||||
// .bds-card-offgrid__icon-container - Icon wrapper (84x84px)
|
||||
// .bds-card-offgrid__icon-image - Image icon styling
|
||||
// .bds-card-offgrid__content - Title and description wrapper
|
||||
// .bds-card-offgrid__title - Card title (32px)
|
||||
// .bds-card-offgrid__description - Card description (18px)
|
||||
//
|
||||
// Note: This file is imported within xrpl.scss after Bootstrap and project
|
||||
// variables are loaded, so $grid-breakpoints, colors, and mixins are available.
|
||||
//
|
||||
// Theme: Site defaults to DARK mode. Light mode uses html.light selector.
|
||||
|
||||
// =============================================================================
|
||||
// Design Tokens
|
||||
// =============================================================================
|
||||
|
||||
// Dimensions (from Figma design spec)
|
||||
$bds-card-offgrid-width: 400px;
|
||||
$bds-card-offgrid-height: 480px;
|
||||
$bds-card-offgrid-padding: 24px;
|
||||
$bds-card-offgrid-icon-container: 84px;
|
||||
$bds-card-offgrid-icon-size: 68px;
|
||||
$bds-card-offgrid-content-gap: 40px;
|
||||
|
||||
// Animation (from Figma design spec)
|
||||
$bds-card-offgrid-transition-duration: 200ms;
|
||||
$bds-card-offgrid-transition-timing: cubic-bezier(0.98, 0.12, 0.12, 0.98);
|
||||
|
||||
// Typography - Title
|
||||
$bds-card-offgrid-title-size: 32px;
|
||||
$bds-card-offgrid-title-line-height: 40px;
|
||||
$bds-card-offgrid-title-letter-spacing: -1px;
|
||||
|
||||
// Typography - Description
|
||||
$bds-card-offgrid-desc-size: 18px;
|
||||
$bds-card-offgrid-desc-line-height: 26.1px;
|
||||
$bds-card-offgrid-desc-letter-spacing: -0.5px;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Dark Mode Colors (Default)
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Neutral variant - Dark Mode
|
||||
$bds-card-offgrid-neutral-default-dark: $gray-500; // #72777E
|
||||
$bds-card-offgrid-neutral-hover-dark: $gray-400; // #8A919A
|
||||
$bds-card-offgrid-neutral-pressed-dark: rgba($gray-500, 0.7); // 70% opacity
|
||||
$bds-card-offgrid-neutral-text-dark: $white; // #FFFFFF
|
||||
|
||||
// Green variant - Dark Mode
|
||||
$bds-card-offgrid-green-default-dark: $green-300; // #21E46B
|
||||
$bds-card-offgrid-green-hover-dark: $green-200; // #70EE97
|
||||
$bds-card-offgrid-green-pressed-dark: $green-400; // #0DAA3E
|
||||
$bds-card-offgrid-green-text-dark: $black; // #000000
|
||||
|
||||
// Disabled - Dark Mode (30% opacity on gray-500)
|
||||
$bds-card-offgrid-disabled-opacity-dark: 0.3;
|
||||
$bds-card-offgrid-disabled-text-dark: $white; // #FFFFFF
|
||||
|
||||
// Focus ring - Dark Mode
|
||||
$bds-card-offgrid-focus-color-dark: $white; // #FFFFFF
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Light Mode Colors
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Neutral variant - Light Mode
|
||||
$bds-card-offgrid-neutral-default-light: $gray-200; // #E6EAF0
|
||||
$bds-card-offgrid-neutral-hover-light: $gray-300; // #CAD4DF
|
||||
$bds-card-offgrid-neutral-pressed-light: $gray-400; // #8A919A
|
||||
$bds-card-offgrid-neutral-text-light: $gray-900; // #111112
|
||||
$bds-card-offgrid-neutral-text-hover-light: $black; // #000000
|
||||
|
||||
// Green variant - Light Mode
|
||||
$bds-card-offgrid-green-default-light: $green-200; // #70EE97
|
||||
$bds-card-offgrid-green-hover-light: $green-300; // #21E46B
|
||||
$bds-card-offgrid-green-pressed-light: $green-400; // #0DAA3E
|
||||
$bds-card-offgrid-green-text-light: $black; // #000000
|
||||
|
||||
// Disabled - Light Mode
|
||||
$bds-card-offgrid-disabled-bg-light: $gray-100; // #F0F3F7
|
||||
$bds-card-offgrid-disabled-text-light: $gray-500; // #72777E
|
||||
|
||||
// Focus ring - Light Mode
|
||||
$bds-card-offgrid-focus-color-light: $gray-900; // #111112
|
||||
|
||||
// =============================================================================
|
||||
// Base Card Styles
|
||||
// =============================================================================
|
||||
|
||||
.bds-card-offgrid {
|
||||
// Reset button/anchor styles
|
||||
appearance: none;
|
||||
border: none;
|
||||
background: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
|
||||
// Card layout
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
width: $bds-card-offgrid-width;
|
||||
height: $bds-card-offgrid-height;
|
||||
padding: $bds-card-offgrid-padding;
|
||||
overflow: hidden;
|
||||
|
||||
// Animation
|
||||
transition: background-color $bds-card-offgrid-transition-duration $bds-card-offgrid-transition-timing,
|
||||
opacity $bds-card-offgrid-transition-duration $bds-card-offgrid-transition-timing;
|
||||
|
||||
// Focus styles - Dark Mode (default)
|
||||
// 1px gap between card and focus ring
|
||||
&:focus {
|
||||
outline: 2px solid $bds-card-offgrid-focus-color-dark;
|
||||
outline-offset: 1px;
|
||||
}
|
||||
|
||||
&:focus:not(:focus-visible) {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: 2px solid $bds-card-offgrid-focus-color-dark;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Overlay (Color wipe animation - "Window Shade" effect)
|
||||
// =============================================================================
|
||||
// Hover in: shade rises from bottom to top (reveals)
|
||||
// Hover out: shade falls from top to bottom (hides)
|
||||
|
||||
.bds-card-offgrid__overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
|
||||
// Default: hidden (shade is "rolled up" at bottom, top is 100% clipped)
|
||||
// When transitioning TO this state, the top inset increases = shade falls down
|
||||
clip-path: inset(100% 0 0 0);
|
||||
transition: clip-path $bds-card-offgrid-transition-duration $bds-card-offgrid-transition-timing;
|
||||
}
|
||||
|
||||
// Hovered state: shade fully raised (visible)
|
||||
// When transitioning TO this state, the top inset decreases = shade rises up
|
||||
.bds-card-offgrid--hovered .bds-card-offgrid__overlay {
|
||||
clip-path: inset(0 0 0 0);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Icon Container
|
||||
// =============================================================================
|
||||
|
||||
.bds-card-offgrid__icon-container {
|
||||
position: relative;
|
||||
z-index: 1; // Above overlay
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: $bds-card-offgrid-icon-container;
|
||||
height: $bds-card-offgrid-icon-container;
|
||||
flex-shrink: 0;
|
||||
|
||||
// Icon sizing
|
||||
> * {
|
||||
max-width: $bds-card-offgrid-icon-size;
|
||||
max-height: $bds-card-offgrid-icon-size;
|
||||
}
|
||||
}
|
||||
|
||||
.bds-card-offgrid__icon-image {
|
||||
width: auto;
|
||||
height: auto;
|
||||
max-width: $bds-card-offgrid-icon-size;
|
||||
max-height: $bds-card-offgrid-icon-size;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Content (Title + Description)
|
||||
// =============================================================================
|
||||
|
||||
.bds-card-offgrid__content {
|
||||
position: relative;
|
||||
z-index: 1; // Above overlay
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $bds-card-offgrid-content-gap;
|
||||
}
|
||||
|
||||
.bds-card-offgrid__title {
|
||||
font-size: $bds-card-offgrid-title-size;
|
||||
font-weight: 300; // Light
|
||||
line-height: $bds-card-offgrid-title-line-height;
|
||||
letter-spacing: $bds-card-offgrid-title-letter-spacing;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.bds-card-offgrid__description {
|
||||
font-size: $bds-card-offgrid-desc-size;
|
||||
font-weight: 300; // Light
|
||||
line-height: $bds-card-offgrid-desc-line-height;
|
||||
letter-spacing: $bds-card-offgrid-desc-letter-spacing;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// DARK MODE (Default)
|
||||
// =============================================================================
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Neutral Variant - Dark Mode
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
.bds-card-offgrid--neutral {
|
||||
background-color: $bds-card-offgrid-neutral-default-dark;
|
||||
color: $bds-card-offgrid-neutral-text-dark;
|
||||
|
||||
// Overlay color for hover wipe
|
||||
.bds-card-offgrid__overlay {
|
||||
background-color: $bds-card-offgrid-neutral-hover-dark;
|
||||
}
|
||||
|
||||
// Pressed state
|
||||
&:active:not(.bds-card-offgrid--disabled) {
|
||||
.bds-card-offgrid__overlay {
|
||||
background-color: $bds-card-offgrid-neutral-pressed-dark;
|
||||
clip-path: inset(0 0 0 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Green Variant - Dark Mode
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
.bds-card-offgrid--green {
|
||||
background-color: $bds-card-offgrid-green-default-dark;
|
||||
color: $bds-card-offgrid-green-text-dark;
|
||||
|
||||
// Overlay color for hover wipe
|
||||
.bds-card-offgrid__overlay {
|
||||
background-color: $bds-card-offgrid-green-hover-dark;
|
||||
}
|
||||
|
||||
// Pressed state
|
||||
&:active:not(.bds-card-offgrid--disabled) {
|
||||
.bds-card-offgrid__overlay {
|
||||
background-color: $bds-card-offgrid-green-pressed-dark;
|
||||
clip-path: inset(0 0 0 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Disabled State - Dark Mode
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
.bds-card-offgrid--disabled {
|
||||
background-color: $bds-card-offgrid-neutral-default-dark;
|
||||
color: $bds-card-offgrid-disabled-text-dark;
|
||||
opacity: $bds-card-offgrid-disabled-opacity-dark;
|
||||
cursor: not-allowed;
|
||||
|
||||
&:focus,
|
||||
&:focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// LIGHT MODE (html.light)
|
||||
// =============================================================================
|
||||
|
||||
html.light {
|
||||
// Focus styles - Light Mode
|
||||
.bds-card-offgrid {
|
||||
&:focus {
|
||||
outline-color: $bds-card-offgrid-focus-color-light;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline-color: $bds-card-offgrid-focus-color-light;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Neutral Variant - Light Mode
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
.bds-card-offgrid--neutral {
|
||||
background-color: $bds-card-offgrid-neutral-default-light;
|
||||
color: $bds-card-offgrid-neutral-text-light;
|
||||
|
||||
// Overlay color for hover wipe
|
||||
.bds-card-offgrid__overlay {
|
||||
background-color: $bds-card-offgrid-neutral-hover-light;
|
||||
}
|
||||
|
||||
// Text color on hover
|
||||
&.bds-card-offgrid--hovered {
|
||||
color: $bds-card-offgrid-neutral-text-hover-light;
|
||||
}
|
||||
|
||||
// Pressed state
|
||||
&:active:not(.bds-card-offgrid--disabled) {
|
||||
.bds-card-offgrid__overlay {
|
||||
background-color: $bds-card-offgrid-neutral-pressed-light;
|
||||
clip-path: inset(0 0 0 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Green Variant - Light Mode
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
.bds-card-offgrid--green {
|
||||
background-color: $bds-card-offgrid-green-default-light;
|
||||
color: $bds-card-offgrid-green-text-light;
|
||||
|
||||
// Overlay color for hover wipe
|
||||
.bds-card-offgrid__overlay {
|
||||
background-color: $bds-card-offgrid-green-hover-light;
|
||||
}
|
||||
|
||||
// Pressed state
|
||||
&:active:not(.bds-card-offgrid--disabled) {
|
||||
.bds-card-offgrid__overlay {
|
||||
background-color: $bds-card-offgrid-green-pressed-light;
|
||||
clip-path: inset(0 0 0 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Disabled State - Light Mode
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
.bds-card-offgrid--disabled {
|
||||
background-color: $bds-card-offgrid-disabled-bg-light;
|
||||
color: $bds-card-offgrid-disabled-text-light;
|
||||
opacity: 1; // Reset opacity, use background color instead
|
||||
|
||||
.bds-card-offgrid__icon-container {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Responsive Styles
|
||||
// =============================================================================
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.bds-card-offgrid {
|
||||
width: 100%;
|
||||
min-height: $bds-card-offgrid-height;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
167
shared/components/CardOffgrid/CardOffgrid.tsx
Normal file
167
shared/components/CardOffgrid/CardOffgrid.tsx
Normal file
@@ -0,0 +1,167 @@
|
||||
import React, { useState, useCallback } from 'react';
|
||||
|
||||
export interface CardOffgridProps {
|
||||
/** Color variant of the card */
|
||||
variant?: 'neutral' | 'green';
|
||||
/** Icon element or image source */
|
||||
icon: React.ReactNode | string;
|
||||
/** Card title (supports multi-line via \n) */
|
||||
title: string;
|
||||
/** Card description text */
|
||||
description: string;
|
||||
/** Click handler */
|
||||
onClick?: () => void;
|
||||
/** Link destination (renders as anchor if provided) */
|
||||
href?: string;
|
||||
/** Disabled state */
|
||||
disabled?: boolean;
|
||||
/** Optional className for custom styling */
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* BDS CardOffgrid Component
|
||||
*
|
||||
* A versatile card component for displaying feature highlights with an icon,
|
||||
* title, and description. Supports neutral and green color variants with
|
||||
* interactive states (hover, focus, pressed, disabled).
|
||||
*
|
||||
* Features a "window shade" color wipe animation:
|
||||
* - Hover in: shade rises from bottom to top (reveals hover color)
|
||||
* - Hover out: shade falls from top to bottom (hides hover color)
|
||||
*
|
||||
* @example
|
||||
* // Basic neutral card
|
||||
* <CardOffgrid
|
||||
* variant="neutral"
|
||||
* icon={<MetadataIcon />}
|
||||
* title="Onchain Metadata"
|
||||
* description="Easily store key asset information."
|
||||
* onClick={() => console.log('clicked')}
|
||||
* />
|
||||
*
|
||||
* @example
|
||||
* // Green card with link
|
||||
* <CardOffgrid
|
||||
* variant="green"
|
||||
* icon="/icons/metadata.svg"
|
||||
* title="Onchain Metadata"
|
||||
* description="Easily store key asset information."
|
||||
* href="/docs/metadata"
|
||||
* />
|
||||
*/
|
||||
export const CardOffgrid: React.FC<CardOffgridProps> = ({
|
||||
variant = 'neutral',
|
||||
icon,
|
||||
title,
|
||||
description,
|
||||
onClick,
|
||||
href,
|
||||
disabled = false,
|
||||
className = '',
|
||||
}) => {
|
||||
// Track hover state for animation
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
const handleMouseEnter = useCallback(() => {
|
||||
if (!disabled) {
|
||||
setIsHovered(true);
|
||||
}
|
||||
}, [disabled]);
|
||||
|
||||
const handleMouseLeave = useCallback(() => {
|
||||
if (!disabled) {
|
||||
setIsHovered(false);
|
||||
}
|
||||
}, [disabled]);
|
||||
|
||||
// Build class names using BEM with bds namespace
|
||||
const classNames = [
|
||||
'bds-card-offgrid',
|
||||
`bds-card-offgrid--${variant}`,
|
||||
disabled && 'bds-card-offgrid--disabled',
|
||||
isHovered && 'bds-card-offgrid--hovered',
|
||||
className,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ');
|
||||
|
||||
// Render icon - supports both React nodes and image URLs
|
||||
const renderIcon = () => {
|
||||
if (typeof icon === 'string') {
|
||||
return (
|
||||
<img
|
||||
src={icon}
|
||||
alt=""
|
||||
className="bds-card-offgrid__icon-image"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
);
|
||||
}
|
||||
return icon;
|
||||
};
|
||||
|
||||
// Split title by newline for multi-line support
|
||||
const renderTitle = () => {
|
||||
const lines = title.split('\n');
|
||||
return lines.map((line, index) => (
|
||||
<React.Fragment key={index}>
|
||||
{line}
|
||||
{index < lines.length - 1 && <br />}
|
||||
</React.Fragment>
|
||||
));
|
||||
};
|
||||
|
||||
// Common content for both button and anchor
|
||||
const content = (
|
||||
<>
|
||||
{/* Hover color wipe overlay */}
|
||||
<span className="bds-card-offgrid__overlay" aria-hidden="true" />
|
||||
|
||||
<span className="bds-card-offgrid__icon-container">
|
||||
{renderIcon()}
|
||||
</span>
|
||||
|
||||
<span className="bds-card-offgrid__content">
|
||||
<span className="bds-card-offgrid__title">
|
||||
{renderTitle()}
|
||||
</span>
|
||||
<span className="bds-card-offgrid__description">
|
||||
{description}
|
||||
</span>
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
|
||||
// Render as anchor if href is provided
|
||||
if (href && !disabled) {
|
||||
return (
|
||||
<a
|
||||
href={href}
|
||||
className={classNames}
|
||||
aria-disabled={disabled}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
{content}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
// Render as button for onClick or disabled state
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={classNames}
|
||||
onClick={disabled ? undefined : onClick}
|
||||
disabled={disabled}
|
||||
aria-disabled={disabled}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
{content}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default CardOffgrid;
|
||||
2
shared/components/CardOffgrid/index.ts
Normal file
2
shared/components/CardOffgrid/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { CardOffgrid, type CardOffgridProps } from './CardOffgrid';
|
||||
export { default } from './CardOffgrid';
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,13 +9,14 @@
|
||||
$white: #FFFFFF;
|
||||
$black: #000000;
|
||||
|
||||
// Gray (Neutral) - Original values (design tokens not ready)
|
||||
$gray-050: #FCFCFD;
|
||||
$gray-100: #F5F5F7;
|
||||
$gray-200: #E0E0E1;
|
||||
$gray-300: #C1C1C2;
|
||||
$gray-400: #A2A2A4;
|
||||
$gray-500: #838386;
|
||||
// Gray (Neutral) - New Design Tokens
|
||||
$gray-100: #F0F3F7; // New design token
|
||||
$gray-200: #E6EAF0; // New design token
|
||||
$gray-300: #CAD4DF; // New design token (default)
|
||||
$gray-400: #8A919A; // New design token
|
||||
$gray-500: #72777E; // New design token
|
||||
// Legacy gray values (600-900 for dark mode)
|
||||
$gray-600: #454549;
|
||||
$gray-700: #343437;
|
||||
$gray-800: #232325;
|
||||
@@ -190,4 +191,4 @@ $light-fg-disabled: $gray-400;
|
||||
$light-form-bg: $gray-200;
|
||||
$light-box-shadow: 0px 5px 20px 0px $gray-300;
|
||||
$light-link-hover-color: $blue-purple-500;
|
||||
$light-standout-bg: $gray-050;
|
||||
$light-standout-bg: $gray-100;
|
||||
|
||||
@@ -91,6 +91,7 @@ $line-height-base: 1.5;
|
||||
@import "../shared/components/Link/_link-icons.scss";
|
||||
@import "../shared/components/Link/_link.scss";
|
||||
@import "../shared/components/Divider/Divider.scss";
|
||||
@import "../shared/components/CardOffgrid/CardOffgrid.scss";
|
||||
@import "_code-tabs.scss";
|
||||
@import "_code-walkthrough.scss";
|
||||
@import "_diagrams.scss";
|
||||
|
||||
Reference in New Issue
Block a user