feat: establish shared Ekklesia design system
CI / verify (pull_request) Successful in 48s
CI / infrastructure-smoke (pull_request) Successful in 20s
CI / migration-smoke (pull_request) Successful in 1m47s

This commit is contained in:
ekklesia
2026-09-30 18:21:09 -05:00
parent 3ca6b2cb32
commit 3a26ef49ad
9 changed files with 336 additions and 110 deletions
+56 -2
View File
@@ -1,9 +1,63 @@
import { type ReactElement, type ReactNode, useEffect, useId, useRef } from "react";
import {
type HTMLAttributes,
type ReactElement,
type ReactNode,
useEffect,
useId,
useRef,
} from "react";
import { classNames } from "./class-names.js";
/** Containers: the panel a screen is built from, the dialog that interrupts it, and the tab strip
* that divides it. */
export interface PageHeaderProps {
readonly title: ReactNode;
readonly description?: ReactNode;
readonly actions?: ReactNode;
readonly eyebrow?: ReactNode;
readonly className?: string;
}
/** The consistent top-level identity and primary-action region for an Ekklesia screen. */
export function PageHeader(props: PageHeaderProps): ReactElement {
return (
<header className={classNames("ek-page-header", props.className)}>
<div className="ek-page-header__copy">
{props.eyebrow === undefined ? undefined : (
<span className="ek-page-header__eyebrow">{props.eyebrow}</span>
)}
<h1 className="ek-page-header__title">{props.title}</h1>
{props.description === undefined ? undefined : (
<p className="ek-page-header__description">{props.description}</p>
)}
</div>
{props.actions === undefined ? undefined : (
<div className="ek-page-header__actions">{props.actions}</div>
)}
</header>
);
}
export interface ActionBarProps extends HTMLAttributes<HTMLDivElement> {
readonly children?: ReactNode;
readonly label?: string;
}
/** A wrapping row of related controls with predictable spacing and alignment. */
export function ActionBar(props: ActionBarProps): ReactElement {
const { children, className, label, ...rest } = props;
return (
<div
{...rest}
{...(label === undefined ? {} : { "aria-label": label })}
className={classNames("ek-action-bar", className)}
>
{children}
</div>
);
}
export interface PanelProps {
readonly title?: ReactNode;
readonly description?: ReactNode;
@@ -26,7 +80,7 @@ export function Panel(props: PanelProps): ReactElement {
>
{hasHeader ? (
<header className="ek-panel__header">
<div>
<div className="ek-panel__heading">
{props.title === undefined ? undefined : (
<h2 className="ek-panel__title">{props.title}</h2>
)}