first commit
This commit is contained in:
81
src/components/ds/Layout.jsx
Normal file
81
src/components/ds/Layout.jsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import * as React from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
/**
|
||||
* Layout helpers.
|
||||
*
|
||||
* These exist so vertical rhythm and grid behaviour are declared once. The
|
||||
* reference pages each invented their own spacing (`space-y-5`, `space-y-6`,
|
||||
* `space-y-8` on sibling pages), which is exactly the drift a design system is
|
||||
* supposed to prevent.
|
||||
*/
|
||||
|
||||
const GAPS = { sm: 'gap-3', default: 'gap-4', lg: 'gap-6' };
|
||||
|
||||
/**
|
||||
* Grid — responsive column layouts with the app's standard breakpoints.
|
||||
*
|
||||
* `cols` is the count at the largest breakpoint; the intermediate steps are
|
||||
* chosen to avoid orphan cards (6 → 2/3/6 rather than 1/3/6).
|
||||
*/
|
||||
/** @param {any} props */
|
||||
export function Grid({ cols = 3, gap = 'default', className, children, ...props }) {
|
||||
const columns = {
|
||||
1: 'grid-cols-1',
|
||||
2: 'grid-cols-1 sm:grid-cols-2',
|
||||
3: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3',
|
||||
4: 'grid-cols-2 md:grid-cols-3 lg:grid-cols-4',
|
||||
5: 'grid-cols-2 md:grid-cols-3 lg:grid-cols-5',
|
||||
6: 'grid-cols-2 md:grid-cols-3 lg:grid-cols-6',
|
||||
}[cols];
|
||||
|
||||
return (
|
||||
<div className={cn('grid', columns, GAPS[gap], className)} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/** Stack — vertical rhythm. `space` maps to the two spacings pages should use. */
|
||||
/** @param {any} props */
|
||||
export function Stack({ space = 'default', className, children, ...props }) {
|
||||
const spacing = {
|
||||
xs: 'space-y-2',
|
||||
sm: 'space-y-3',
|
||||
default: 'space-y-5',
|
||||
lg: 'space-y-6',
|
||||
xl: 'space-y-8',
|
||||
}[space];
|
||||
|
||||
return (
|
||||
<div className={cn(spacing, className)} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* PageSection — a titled region of a page. Pairs a SectionHeader-style label
|
||||
* with its content at consistent spacing.
|
||||
*/
|
||||
/** @param {any} props */
|
||||
export function PageSection({ title, subtitle, actions, level = 'overline', className, children }) {
|
||||
return (
|
||||
<section className={cn('space-y-3', className)}>
|
||||
{title && (
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
{level === 'overline' ? (
|
||||
<h2 className="text-overline uppercase text-ink-4 font-heading">{title}</h2>
|
||||
) : (
|
||||
<div>
|
||||
<h2 className="text-title-lg font-heading text-ink-1">{title}</h2>
|
||||
{subtitle && <p className="text-body-sm text-ink-3 mt-0.5">{subtitle}</p>}
|
||||
</div>
|
||||
)}
|
||||
{actions && <div className="flex items-center gap-2 shrink-0">{actions}</div>}
|
||||
</div>
|
||||
)}
|
||||
{children}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user