W0 web foundation: Vite + React SPA under /app/, sync-pull ledger, auth, tokens, shared components
Build Kvitt images / build (push) Failing after 1h14m5s
Build Kvitt images / build (push) Failing after 1h14m5s
web/ is the browser client's scaffold: Vite 8, React 19, TypeScript 5.9 strict, React Router 7 (basename /app, every fixed route with a lazy placeholder page), TanStack Query 5 (refetch on focus and every 60 s), openapi-typescript types generated from contracts/openapi.yaml with openapi-fetch. Reads go through GET /sync (no REST reads exist for the ledger, SPEC §7): useLedger() pulls from cursor 0 and then incrementally, restarting on a new epoch or cursor_ahead. Login pairs the browser as "Web · <browser>" with an invite code (E-3); every call sends the SPEC §14.3.1 headers, a 401 signs out. kvitt-domain's split, balances, allocation and settlement rules are ported to web/src/lib and pass every case of contracts/test-vectors. Design tokens from design-spec §2 in tokens.css (light-dark()), Material Symbols Rounded (Apache-2.0) as generated inline SVG, sv/en catalogs generated from docs/ux/strings.csv plus web-strings.csv. Gate: cd web && npm run check. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
import type { ButtonHTMLAttributes, ReactNode } from 'react';
|
||||
import { Link, type LinkProps } from 'react-router';
|
||||
import styles from './Button.module.css';
|
||||
|
||||
export type ButtonVariant = 'filled' | 'tonal' | 'outlined' | 'text' | 'danger';
|
||||
export type ButtonSize = 'small' | 'medium';
|
||||
|
||||
interface Common {
|
||||
variant?: ButtonVariant;
|
||||
size?: ButtonSize;
|
||||
/** Leading icon, usually an <Icon size={20} />. */
|
||||
icon?: ReactNode;
|
||||
}
|
||||
|
||||
export function buttonClassName(variant: ButtonVariant = 'filled', size: ButtonSize = 'small', extra?: string): string {
|
||||
return [styles.button, styles[variant], styles[size], extra].filter(Boolean).join(' ');
|
||||
}
|
||||
|
||||
/** design-spec §3.1 buttons: full radius, small 40 px or medium 56 px, label-large; 48 px target on touch screens. */
|
||||
export function Button({
|
||||
variant,
|
||||
size,
|
||||
icon,
|
||||
className,
|
||||
children,
|
||||
type = 'button',
|
||||
...rest
|
||||
}: Common & ButtonHTMLAttributes<HTMLButtonElement>) {
|
||||
return (
|
||||
<button type={type} className={buttonClassName(variant, size, className)} {...rest}>
|
||||
{icon}
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
/** A link that looks like a Button (navigation, not an action). */
|
||||
export function ButtonLink({ variant, size, icon, className, children, ...rest }: Common & LinkProps) {
|
||||
return (
|
||||
<Link className={buttonClassName(variant, size, className)} {...rest}>
|
||||
{icon}
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user