import { SimpleGrid, Alert, Anchor, Badge, Box, Button, Card, Checkbox, Divider, Group, Paper, Radio, Select, Stack, Switch, Table, Tabs, Text, Textarea, TextInput, ThemeIcon, Title, useMantineTheme, } from '@mantine/core'; import { IconAlertTriangle, IconInbox } from '@tabler/icons-react'; import { SkipLink, MAIN_CONTENT_ID } from '../layout/SkipLink'; import { StatusBadge } from '../feedback/StatusBadge'; import { WaitingFor } from '../data/WaitingFor'; import { StatTile } from '../data/StatTile'; /** * Fixed clock. The gallery is screenshotted by the visual suite, so a tile * reading "3d" must read "3d" tomorrow too — `Date.now()` would rewrite the * baseline every day. */ const GALLERY_NOW = '2026-08-21T00:00:00.000Z'; const daysAgo = (n: number) => new Date(Date.parse(GALLERY_NOW) - n * 86_400_000).toISOString(); /** * Every primitive the theme controls, on one page. * * This is the visual-regression surface for theme work. A real feature page * renders a handful of primitives and only their happy states; a theme change * that breaks `Button disabled` or `TextInput error` would sail past it. This * renders all of them, including the states nothing else shows, so a screenshot * diff says precisely what a theme edit did. * * Deliberately free of data, auth and network so it cannot flake: mounted at * `/__gallery` outside the protected routes, it needs no API and no database. * * Not part of the product. Excluded from the nav on purpose. */ const SWATCH_SHADES = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] as const; function Section({ title, children }: { title: string; children: React.ReactNode }) { return ( {title} {children} ); } /** A full 0–9 ramp, so a palette swap is visible shade by shade. */ function ColorRamp({ name }: { name: string }) { return ( {name} {SWATCH_SHADES.map((shade) => ( {shade} ))} ); } export function ThemeGallery() { const theme = useMantineTheme(); const paletteNames = Object.keys(theme.colors).filter((c) => // The stock Mantine ramps are noise here; show the ones this app defines, // plus gray because overriding it is the riskiest single theme change. c.startsWith('ema') || c === 'gray', ); return ( <> {/* Mirrors the real app shells, so the skip-link contract is testable without a session. */} Theme Gallery Visual-regression surface for theme changes. Not a product page.
Heading 1 — Maritime licensing Heading 2 — Seafarer registry Heading 3 — Vessel registration Heading 4 — Certificate of competency Heading 5 — Sea service record Body text. The quick brown fox jumps over the lazy dog. Small dimmed text, used for subtitles and helper copy. Extra-small text, used for metadata. {/* Mixed-script line: the case a [lang] font swap would break. */} Amharic: አበበ ግርማ — SF-2024-0001 — የአዲስ አበባ ወደብ {/* Ge'ez at heading size, where its taller ascenders clip first. */} የኢትዮጵያ ማሪታይም ባለስልጣን የመርከበኞች መዝገብ — Seafarer Registry Monospace: ET-IMO-0231 · 28,450 GT
{paletteNames.map((name) => ( ))}
Card withBorder Radius and shadow come from the theme. Paper withBorder Default radius applies here too. Paper shadow=md Shadow ramp check.
Default Active Pending Suspended Expiring Draft Filled Outline Dot