@navio-maritime/ui
v0.1.0
Published
Navio Maritime shared design system: tokens + components for every Navio product
Maintainers
Readme
type: spec title: '@navio-maritime/ui — shared design system + components' status: active owner: Stream A updated: 2026-04-25 tags: [package, ui, design-system, m4] phase: M4
@navio-maritime/ui
Navio Maritime shared design system. Design tokens, base styles, and React components — one source of truth for every Navio product.
Version: 0.1.0
Status: Greenfield — T-1 implementation complete, awaiting @navio npm scope (Dylan M2) to publish.
Installation
# In a consumer app (BEACON, Hub, etc.)
pnpm add @navio-maritime/ui react react-dom lucide-reactAdd the Tailwind preset to tailwind.config.ts:
import navioPreset from '@navio-maritime/ui/tailwind-preset';
export default {
presets: [navioPreset],
content: ['./src/**/*.{ts,tsx}', './node_modules/@navio-maritime/ui/dist/**/*.js'],
};Import the base styles once (e.g., in app/layout.tsx or main.tsx):
import '@navio-maritime/ui/styles.css';Components
Button
import { Button } from '@navio-maritime/ui';
<Button variant="primary" size="md" onClick={handleAcknowledge}>
Acknowledge
</Button>
<Button variant="destructive" loading>
Deleting…
</Button>
<Button variant="ghost" leftIcon={<RefreshCw size={16} />}>
Refresh
</Button>Variants: primary | secondary | ghost | destructive
Sizes: sm | md | lg
Props: loading, disabled, leftIcon, rightIcon, fullWidth, asChild (Radix Slot)
Input
import { Input } from '@navio-maritime/ui';
import { Search } from 'lucide-react';
<Input placeholder="IMO Number" />
<Input error placeholder="Invalid value" />
<Input size="sm" icon={<Search size={14} />} placeholder="Search…" />Variants: default | error
Sizes: sm | md
Props: error, icon (renders left-side icon)
Card
import { Card, CardHeader, CardBody, CardFooter } from '@navio-maritime/ui';
<Card variant="elevated">
<CardHeader>Main Engine</CardHeader>
<CardBody>Running hours: 4,200. Last service: 2026-03-01.</CardBody>
<CardFooter>
<Button size="sm">View tasks</Button>
</CardFooter>
</Card>;Variants: default | elevated | interactive
Subcomponents: CardHeader, CardBody, CardFooter
Dialog
import {
Dialog,
DialogTrigger,
DialogContent,
DialogHeader,
DialogTitle,
DialogClose,
} from '@navio-maritime/ui';
<Dialog>
<DialogTrigger asChild>
<Button>Complete task</Button>
</DialogTrigger>
<DialogContent size="md">
<DialogHeader>
<DialogTitle>Confirm completion</DialogTitle>
<DialogClose />
</DialogHeader>
<p>Mark the oil change as completed?</p>
<div style={{ display: 'flex', gap: '8px', justifyContent: 'flex-end' }}>
<DialogClose asChild>
<Button variant="ghost">Cancel</Button>
</DialogClose>
<Button variant="primary">Confirm</Button>
</div>
</DialogContent>
</Dialog>;Built on Radix UI Dialog — focus-trapped, keyboard-dismissible, ARIA-compliant.
Sizes: sm (max-w-md) | md (max-w-lg) | lg (max-w-2xl)
Subcomponents: DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogClose
Form
import { Form, FormField, FormLabel, FormError, FormHelp } from '@navio-maritime/ui';
import { Input } from '@navio-maritime/ui';
<Form onSubmit={handleSubmit}>
<FormField>
<FormLabel htmlFor="imo">IMO Number</FormLabel>
<Input id="imo" name="imo" required />
<FormHelp>7 digits, found on Safety Equipment Certificate</FormHelp>
</FormField>
<FormField>
<FormLabel htmlFor="email">Email</FormLabel>
<Input id="email" type="email" error={!!errors.email} />
{errors.email && <FormError>{errors.email}</FormError>}
</FormField>
<Button type="submit">Save</Button>
</Form>;Composition primitives — no magic, just consistent spacing and label/error wiring.
Subcomponents: FormField, FormLabel, FormError (role=alert), FormHelp
Design tokens (programmatic access)
import { tokens } from '@navio-maritime/ui';
tokens.colors.navio.maritime; // '#1E3A5F'
tokens.colors.status.distress; // '#EF4444'
tokens.shadows.md; // '0 4px 6px...'
tokens.motion.duration.fast; // '150ms'Development
cd packages/ui-navio
# Build ESM + CJS + types
pnpm build
# Watch mode
pnpm dev
# Type-check
pnpm typecheck
# Tests (Vitest + Testing Library)
pnpm test
# Storybook at :6006
pnpm storybookDesign philosophy
- Maritime professional — calm, fast, unambiguous; crew use these tools at sea
- Dark mode first — default dark, light supported
- Reserved color — status colors (red/green/amber) used sparingly and consistently
- System fonts — no web-font load; fast on slow satellite connections
- Touch-friendly — minimum 44px touch targets for tablet/mobile use
What's in v0.1.0
| Component | Variants |
| ---------- | ------------------------------------------------------- |
| <Button> | primary, secondary, ghost, destructive · sm, md, lg |
| <Input> | default, error · sm, md · icon |
| <Card> | default, elevated, interactive + CardHeader/Body/Footer |
| <Dialog> | sm, md, lg (Radix-based, accessible) |
| <Form> | FormField, FormLabel, FormError, FormHelp (composition) |
Deferred to v0.2.0: Table, Select, DatePicker, Tabs, Toast, Tooltip, Badge, Skeleton, Spinner, Sidebar, NavBar.
Publishing
Requires Dylan to register @navio scope on npmjs.org (runbook _COMMS/architecture/runbooks/01-npm-scope.md).
After that: pnpm publish from this directory, or via the GitHub Actions workflow.
Consumers
- BEACON — from M8 first commit (H1 2027)
- Hub — from M6 launch (alongside MANTIS 2026-06-19)
- NavioWorks + MANTIS — incremental adoption post-launch
