Skip to content

Components

@memoturn/ui is the shared component library used by the Memoturn dashboard and this docs site. It’s built on shadcn/ui new-york-v4, Radix UI primitives, Tailwind CSS v4, and a brand color overlay defined in tokens.css.

The library is workspace-internal today ("private": true). There’s no npm release.

The 57 component primitives are sourced verbatim from shadcn/ui’s new-york-v4 registry. Brand identity comes through three vectors:

  1. --primary: brand teal (lagoon-deep light / lagoon dark) appears on every button, focus ring, accent, and active state.
  2. Display typography: Inter Tight headings and Manrope body give the surface its voice without painting it.
  3. Atmospheric decoration: .atoll-band topographic texture appears on hero/CTA/empty surfaces; brand gradients (--gradient-lagoon, --gradient-atoll) on hero badges and the user avatar.

Structural surfaces (--background, --card, --popover, --border, --muted, --accent) use shadcn’s neutral oklch tokens: pure white in light, pure dark in dark mode, soft gray borders. The docs landing (HeroCard, LinkChip, FeatureCard, CTA) uses --docs-panel-bg (sand-tinted page-white in light, card-tinted in dark) with hairline --docs-line borders; the closing-band CTA lights up the brand --gradient-atoll as the page’s single brand moment.

Defined in packages/ui/src/styles/tokens.css. Composed via Tailwind v4 @theme inline so every token is addressable as a Tailwind utility (bg-card, text-primary, border-input, etc.).

TokenLightDarkUse
--lagoon#4fb8b2#60d7cfBright accent (bg-lagoon)
--lagoon-deep#328f97#8de5dbDeep accent (text-lagoon-deep)
--palm#2f6a4a#6ec89aSecondary accent / success
--sand / --foamwarm palesdark scaledAtmospheric tints
--accent-warm#e89456#f5b169Warm accent (warnings, CTA highlights)

These exist for hero/CTA decoration. Don’t reach for them in primitives. Primitives consume the semantic tokens below, which already route through --primary etc.

FamilyTokens
Surfaces--background, --card, --popover, --surface (muted panels), --sidebar
Text--foreground, --card-foreground, --muted-foreground, --popover-foreground
Action--primary (brand teal), --secondary, --accent, --destructive, --success, --warning
Form--border, --input, --ring (also brand teal)
Code--code, --code-foreground, --code-highlight, --code-number
Chart--chart-1 through --chart-5 (brand-sequenced)
Selection--selection, --selection-foreground
Overlay--overlay, --overlay-strong, --overlay-subtle (three-tier)

Every token has a dark counterpart that flips on :root[data-theme="dark"], .dark class, or @media (prefers-color-scheme: dark).

--radius defaults to 0.375rem (~6px). Derived scale: --radius-sm through --radius-4xl via calc(var(--radius) * N). Use Tailwind utilities: rounded-sm, rounded-md, rounded-lg, rounded-xl.

Tailwind’s shadow-xs / shadow-sm / shadow-md / shadow-lg are overridden with brand green-tinted shadows in light mode, neutral darkness in dark mode. Every primitive that uses these utilities picks up the brand atmosphere automatically.

  • Body / UI: Manrope, --font-sans / font-sans
  • Display / heading: Inter Tight, --font-heading / font-heading
  • Code: system mono stack, --font-mono / font-mono

Manrope provides humanist warmth in body copy. Inter Tight (used by Linear, Vercel, Anthropic) provides modern display character without the cartoony ball terminals of Fraunces.

Two utility classes give you the brand voice:

  • .kicker: small uppercase tracked label (KICKER LIKE THIS). Composes with margin/color modifiers: class="kicker mb-2 text-primary".
  • .display-title: Inter Tight semibold, tight tracking, 1.05 line-height. Use for hero/section/card titles.

These define the dashboard’s visual rhythm without touching component primitives:

  • .page-wrap: fluid full-width container, soft cap at 1800px, padding scales clamp(1rem, 3vw, 3rem).
  • .atoll-band: topographic concentric-arc texture at 6% opacity with a radial mask; compose on cards or sections for ambient depth.
  • .rise-in: entrance animation; respects prefers-reduced-motion.
  • .shimmer: left-to-right brand-tinted gradient sweep; powers the Skeleton primitive.
  • .nav-link: underline indicator that grows from left on hover/active; brand-teal stroke.

All 57 shadcn new-york-v4 components are exported from @memoturn/ui. Categories below highlight the most-used; the full list is in packages/ui/src/index.ts.

Button, Input, Textarea, Select, NativeSelect, Checkbox, RadioGroup, Switch, Slider, Label, Form (react-hook-form integration), Field, FieldGroup, InputGroup, InputOTP, Combobox, Toggle, ToggleGroup, ButtonGroup.

Every form control shares one focus language: ring-2 ring-ring/30 on focus, hover:border-ring/40 preview, brand-teal ring (because --ring resolves to --primary).

Dialog, AlertDialog, Sheet, Drawer, Popover, HoverCard, Tooltip, DropdownMenu, ContextMenu, Menubar, Command (CommandDialog for ⌘K palettes).

All use bg-popover for content and the brand-tinted overlay scrim. data-slot attributes on every element for inspect-and-style.

Card (+ CardHeader/CardTitle/CardDescription/CardContent/CardFooter/CardAction), Sidebar, Resizable panels, ScrollArea, Separator, AspectRatio, Tabs.

CardTitle / DialogTitle / SheetTitle / DrawerTitle / EmptyTitle use font-heading (Inter Tight). Every modal display moment is in the brand voice.

Table, Badge, Avatar (+ AvatarFallback / AvatarBadge / AvatarGroup), Item (data row primitive), Empty (EmptyTitle/EmptyDescription/EmptyMedia), Pagination, Breadcrumb, Accordion, Collapsible, Calendar, Carousel, Chart (recharts wrapper), Progress, Skeleton, Spinner, Kbd.

Badge extends shadcn defaults with three brand variants (dense primary tint, lexical palm tint, hot warm tint) used for search-leg color coding. Skeleton uses .shimmer for brand-tinted loading.

Toaster (Sonner-backed) + the toast helper. Mount once at the app root:

import { Toaster } from "@memoturn/ui";
export function RootLayout() {
return (
<>
{/* …app… */}
<Toaster />
</>
);
}

Then fire from anywhere:

import { toast } from "@memoturn/ui";
await revokeKey(id);
toast.success(`Revoked "${name}"`);
// or with a description
toast.error({ title: "Revoke failed", description: err.message });

The helper exposes success, error, info, and dismiss. Each type gets a left-border color stripe (success → palm, info → primary, warning → accent-warm, error → destructive) and a brand-tinted icon.

Higher-level building blocks specific to the Memoturn brand voice. They compose primitives plus brand decoration (atoll-band, gradients, kicker labels).

ComponentSourceUse
HeroHero.tsxLanding / section hero with kicker, title, description, action slots.
FeatureCardFeatureCard.tsxIcon + kicker + title + description + footer; lifts on hover.
CTACTA.tsxBrand-gradient call-to-action panel with primary + secondary actions.
EmptyStateEmptyState.tsxCentered empty state with BrandMark, title, description, action.
CodeBlockCodeBlock.tsxSnippet block with copy-to-clipboard button.
LogoLogo.tsx4 variants: full, mark, wordmark, chip.
BrandMarkBrandMark.tsxStandalone SVG mark.
ThemeToggleThemeToggle.tsxLight / dark / auto switcher; persists to localStorage.

The dashboard wires apps/app/components.json so the shadcn CLI installs into packages/ui/src/components/:

Terminal window
pnpm exec shadcn add <component>

After install, normalize the imports to relative paths (@/lib/utils../lib/utils, @/components/ui/..../...) and re-export from packages/ui/src/index.ts.

Both the Memoturn dashboard (TanStack Start on Workers) and this docs site (Astro + Starlight) consume tokens.css directly. The dashboard mounts via Tailwind v4’s @theme inline directive; the docs site @imports and adds back-compat aliases for older brand-tinted decoration.

To embed @memoturn/ui in a third app, import the tokens once at the top of your global stylesheet, then add the Tailwind layer:

@import "tailwindcss";
@import "tw-animate-css";
@import "@memoturn/ui/styles/tokens.css";
@import "@memoturn/ui/styles/base.css";
@source "../node_modules/@memoturn/ui/src/components/**/*.{ts,tsx}";
@custom-variant dark (&:is([data-theme="dark"] *, .dark *));
  • Every interactive primitive has a visible focus-visible ring driven by --ring (brand teal).
  • Radix-backed components inherit Radix’s keyboard and screen-reader support.
  • Form inputs respect aria-invalid (destructive ring + border) and pair with <Label htmlFor>.
  • Empty and error boundaries use semantic headings; modal titles use proper Dialog.Title.
  • Reduced motion: .rise-in and .shimmer honor prefers-reduced-motion: reduce.

If you find a component that fails an axe-core check, please email support@memoturn.ai. Accessibility regressions are bugs, not enhancements.