chore: sync codebase remediation, gameplay systems, and docs

Security & infrastructure:
- Remove unused services/ (auth, spacetimedb) and auth.db
- Add .env.example template, expand .gitignore for env/db files
- Add GitHub Actions CI + commitlint config and workflows
- Add manual vendor chunking and source maps to docs/site vite configs

Shared UI & docs app:
- Add ARIA props and focus-visible rings to Button/Panel
- Add ButtonAsLink primitive; use shared Button in NotFound
- Wire @void-nav/ui into docs app; refresh content pages
- Replace Todo page with Kanban board

Gameplay (Bevy):
- Add ai module (behavior, faction, navigation, perception, spawning, states)
- Add narrative module (events, history, synthesis, ui)
- Refine galaxy contents and in-system flight/scene systems
This commit is contained in:
2026-06-16 11:49:13 -04:00
parent 98c2ba59df
commit 57633addfe
60 changed files with 5084 additions and 2473 deletions

View File

@@ -6,49 +6,95 @@ type BaseProps = {
children: ReactNode;
className?: string;
tone?: ButtonTone;
/** Accessible label for screen readers (overrides children text) */
"aria-label"?: string;
/** Indicates if this button is part of a larger grouping */
"aria-haspopup"?: boolean | "menu" | "listbox" | "tree" | "grid" | "dialog";
/** Identifies the element controlled by this button */
"aria-controls"?: string;
/** Identifies the expanded state (for toggle buttons) */
"aria-expanded"?: boolean;
/** Identifies the pressed state (for toggle buttons) */
"aria-pressed"?: boolean;
/** Keyboard navigation: explicit tab index */
tabIndex?: number;
};
type NativeButtonProps = BaseProps &
ButtonHTMLAttributes<HTMLButtonElement> & {
href?: never;
to?: never;
};
type AnchorButtonProps = BaseProps &
AnchorHTMLAttributes<HTMLAnchorElement> & {
href: string;
to?: never;
};
/**
* Button component with accessibility support and multiple visual tones.
*
* Can be rendered as:
* - `<button>` (default)
* - `<a>` (when `href` is provided)
*
* Note: For React Router links, use the ButtonAsLink component instead.
*
* @example
* // Native button
* <Button tone="primary" onClick={handleClick}>Click me</Button>
*
* @example
* // External link
* <Button href="https://example.com" tone="secondary">External</Button>
*/
export type ButtonProps = NativeButtonProps | AnchorButtonProps;
const tones: Record<ButtonTone, string> = {
primary: "border-accent bg-accent text-bg hover:bg-accent-hover",
secondary: "border-border bg-surface-raised text-fg hover:border-border-light hover:bg-surface-hover",
ghost: "border-transparent bg-transparent text-cyan hover:border-border-light hover:bg-surface",
primary: "border-accent bg-accent text-bg hover:bg-accent-hover focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-bg",
secondary: "border-border bg-surface-raised text-fg hover:border-border-light hover:bg-surface-hover focus-visible:ring-2 focus-visible:ring-border focus-visible:ring-offset-2 focus-visible:ring-offset-bg",
ghost: "border-transparent bg-transparent text-cyan hover:border-border-light hover:bg-surface focus-visible:ring-2 focus-visible:ring-cyan focus-visible:ring-offset-2 focus-visible:ring-offset-bg",
};
export function Button({ children, className = "", tone = "secondary", ...props }: ButtonProps) {
const classes = [
/**
* Shared button styles and accessibility support.
* Used by both Button and ButtonAsLink components.
*/
export function useButtonProps(tone: ButtonTone = "secondary", className: string = "") {
return [
"inline-flex min-h-10 cursor-pointer items-center justify-center gap-2 rounded-md border px-4 py-2 text-sm font-semibold transition-colors duration-150 disabled:cursor-not-allowed disabled:opacity-55",
"focus-visible:outline-none",
tones[tone],
className,
]
.filter(Boolean)
.join(" ");
}
if ("href" in props) {
const anchorProps = props as Omit<AnchorButtonProps, keyof BaseProps>;
export function Button({ children, className = "", tone = "secondary", ...props }: ButtonProps) {
const classes = useButtonProps(tone, className);
// Extract accessibility props to spread on the rendered element
const { "aria-label": ariaLabel, "aria-haspopup": ariaHaspopup, "aria-controls": ariaControls, "aria-expanded": ariaExpanded, "aria-pressed": ariaPressed, tabIndex, ...restProps } = props;
const ariaProps = { "aria-label": ariaLabel, "aria-haspopup": ariaHaspopup, "aria-controls": ariaControls, "aria-expanded": ariaExpanded, "aria-pressed": ariaPressed, tabIndex };
// Handle native anchor (href prop)
if ("href" in restProps && restProps.href) {
const anchorProps = restProps as Omit<AnchorButtonProps, keyof BaseProps>;
return (
<a className={classes} {...anchorProps}>
<a className={classes} {...anchorProps} {...ariaProps}>
{children}
</a>
);
}
const buttonProps = props as Omit<NativeButtonProps, keyof BaseProps>;
// Handle native button
const buttonProps = restProps as Omit<NativeButtonProps, keyof BaseProps>;
return (
<button className={classes} {...buttonProps}>
<button className={classes} type="button" {...buttonProps} {...ariaProps}>
{children}
</button>
);