Skip to main content

Overview

Nightwatch UI is Skiff’s design system implementation - a modern, accessible component library built with React, styled-components, and Material UI. It provides a complete set of UI primitives for building consistent user interfaces across the Skiff platform. The library features dual light/dark theming, responsive design patterns, and comprehensive TypeScript support.

Installation

yarn add nightwatch-ui

Import Styles

import 'nightwatch-ui/dist/esm/index.css';

Basic Usage

import { Button, Dialog, InputField } from 'nightwatch-ui';
import { Size, Type, ThemeMode } from 'nightwatch-ui';

Core Components

Button

The Button component comes in multiple variants for different use cases.
import { Button } from 'nightwatch-ui';

function Example() {
  return (
    <Button
      onClick={() => console.log('Clicked!')}
      type={Type.PRIMARY}
      size={Size.MEDIUM}
    >
      Click me
    </Button>
  );
}

Button Props

children
string
required
Button text content
onClick
(e: React.MouseEvent) => void | Promise<void>
required
Click event handler
type
Type
default:"Type.PRIMARY"
Button variant: PRIMARY, SECONDARY, TERTIARY, DESTRUCTIVE
size
Size
default:"Size.MEDIUM"
Button size: X_SMALL, SMALL, MEDIUM, LARGE
icon
Icon | IconComponent
Optional start icon
loading
boolean
Shows loading spinner
disabled
boolean
Disables button interaction
fullWidth
boolean
Expands button to full container width
tooltip
string
Tooltip text on hover
forceTheme
ThemeMode
Force light or dark theme

Dialog

Modal dialogs for user interactions and confirmations.
import { Dialog, DialogType, ButtonGroupItem } from 'nightwatch-ui';

function ConfirmDialog() {
  const [open, setOpen] = useState(false);

  return (
    <Dialog
      open={open}
      onClose={() => setOpen(false)}
      type={DialogType.CONFIRM}
      title="Delete file?"
      description="This action cannot be undone."
    >
      <ButtonGroupItem
        label="Cancel"
        onClick={() => setOpen(false)}
      />
      <ButtonGroupItem
        label="Delete"
        type={Type.DESTRUCTIVE}
        onClick={async () => {
          await deleteFile();
          setOpen(false);
        }}
      />
    </Dialog>
  );
}

Dialog Props

open
boolean
required
Dialog visibility state
onClose
() => void | Promise<void>
required
Close handler
children
ButtonGroupItemComponent[] | React.ReactNode
required
Dialog action buttons or custom content
type
DialogType
default:"DialogType.DEFAULT"
Dialog variant: CONFIRM, DEFAULT, INPUT, PROMOTIONAL, SEARCH, SETTINGS, LANDSCAPE
title
string
Dialog title text
description
string
Dialog description text
inputField
InputComponent | InputComponent[]
Input fields for the dialog
loading
boolean
Shows loading spinner
hideCloseButton
boolean
Hides the close (X) button
disableOffClick
boolean
Prevents closing on outside click

InputField

Flexible input component with support for icons, validation, and various styles.
import { InputField, InputType } from 'nightwatch-ui';

function Example() {
  const [value, setValue] = useState('');

  return (
    <InputField
      value={value}
      onChange={(e) => setValue(e.target.value)}
      placeholder="Enter your email"
      type={InputType.EMAIL}
    />
  );
}

InputField Props

value
string
default:""
Input value
onChange
(e: React.ChangeEvent<HTMLInputElement>) => void
Change event handler
placeholder
string
Placeholder text
type
InputType
default:"InputType.DEFAULT"
Input type: DEFAULT, EMAIL, PASSWORD, NUMBER, TEL, URL
icon
Icon
Start icon
endAdornment
Icon | React.ReactNode | Array
End icon or custom element
error
boolean | string
Error state or error message
helperText
string
Helper text below input
disabled
boolean
Disables input
readOnly
boolean
Makes input read-only
variant
InputFieldVariant
default:"DEFAULT"
Style variant: DEFAULT, GHOST
size
Size
default:"Size.MEDIUM"
Input size
Contextual menus with keyboard navigation support.
import { Dropdown, DropdownItem } from 'nightwatch-ui';

function ContextMenu() {
  const [showDropdown, setShowDropdown] = useState(false);
  const buttonRef = useRef<HTMLDivElement>(null);

  return (
    <>
      <div ref={buttonRef} onClick={() => setShowDropdown(true)}>
        Options
      </div>
      <Dropdown
        buttonRef={buttonRef}
        showDropdown={showDropdown}
        setShowDropdown={setShowDropdown}
        portal
      >
        <DropdownItem label="Edit" icon={Icon.Edit} onClick={handleEdit} />
        <DropdownItem label="Share" icon={Icon.Share} onClick={handleShare} />
        <DropdownItem 
          label="Delete" 
          icon={Icon.Trash}
          color={DropdownItemColor.DESTRUCTIVE}
          onClick={handleDelete}
        />
      </Dropdown>
    </>
  );
}
showDropdown
boolean
default:true
Dropdown visibility state
setShowDropdown
(open: boolean) => void
required
Update dropdown state
buttonRef
React.MutableRefObject<HTMLDivElement | null>
Reference to anchor element
portal
boolean
Render in portal (recommended for better positioning)
fullWidth
boolean
Match anchor width
inputField
InputComponent
Search input at top of dropdown
maxHeight
number | string
Maximum dropdown height

Tabs

Tabbed navigation component with smooth animations.
import { Tabs, TabsSize } from 'nightwatch-ui';

function TabExample() {
  const [activeTab, setActiveTab] = useState('overview');

  return (
    <Tabs
      size={Size.MEDIUM}
      tabs={[
        {
          label: 'Overview',
          active: activeTab === 'overview',
          onClick: () => setActiveTab('overview'),
          icon: Icon.Home
        },
        {
          label: 'Settings',
          active: activeTab === 'settings',
          onClick: () => setActiveTab('settings'),
          icon: Icon.Settings
        }
      ]}
    />
  );
}

Toast

Toast notifications for temporary messages.
import { Toast } from 'nightwatch-ui';
import { useToast } from 'nightwatch-ui';

function NotificationExample() {
  const { enqueueToast } = useToast();

  const showSuccess = () => {
    enqueueToast({
      title: 'Success',
      body: 'Your changes have been saved',
      type: Type.SUCCESS
    });
  };

  return <Button onClick={showSuccess}>Save</Button>;
}

Toggle

Toggle switch component.
import { Toggle } from 'nightwatch-ui';

function SettingsToggle() {
  const [enabled, setEnabled] = useState(false);

  return (
    <Toggle
      checked={enabled}
      onChange={setEnabled}
      label="Enable notifications"
    />
  );
}

Complete Component List

Nightwatch UI includes the following components:
  • Button Components: Button, IconButton, ButtonGroup, ButtonGroupItem
  • Input Components: InputField, TextArea, Select, CodeInput
  • Display Components: Avatar, Chip, MonoTag, Typography, Skeleton, Facepile
  • Feedback Components: Dialog, Toast, CircularProgress, Banner
  • Navigation Components: Tabs, Dropdown, DropdownItem, DropdownSubmenu
  • Overlay Components: Tooltip, Surface, Portal
  • Layout Components: Divider
  • Form Components: Toggle
  • Icon System: Icons with 100+ icons

Theming

Nightwatch UI uses CSS custom properties for theming, with full light and dark mode support.

Using Themes

import { ThemeMode } from 'nightwatch-ui';

// Force dark theme on a component
<Button forceTheme={ThemeMode.DARK}>
  Dark Button
</Button>

// Most components support forceTheme prop
<Dialog forceTheme={ThemeMode.LIGHT}>
  {/* Light themed dialog */}
</Dialog>

Theme Variables

The theme system provides semantic color tokens:
  • Text: --text-primary, --text-secondary, --text-tertiary, --text-disabled, --text-link, --text-destructive
  • Icons: --icon-primary, --icon-secondary, --icon-disabled
  • Backgrounds: --bg-l0-solid, --bg-l1-solid, --bg-l2-solid, --bg-l3-solid
  • Borders: --border-primary, --border-secondary, --border-hover, --border-active
  • Accents: --accent-orange-primary, --accent-green-primary, --accent-blue-primary, etc.

Custom Styling

Components support styled-components and custom className props:
import styled from 'styled-components';
import { Button } from 'nightwatch-ui';

const CustomButton = styled(Button)`
  border-radius: 20px;
  text-transform: uppercase;
`;

Typography System

import { Typography, TypographySize, TypographyWeight } from 'nightwatch-ui';

function Example() {
  return (
    <Typography 
      size={TypographySize.H3}
      weight={TypographyWeight.MEDIUM}
      color="secondary"
    >
      Welcome to Skiff
    </Typography>
  );
}

Icon System

import { Icons, Icon } from 'nightwatch-ui';

function Example() {
  return (
    <>
      <Icons icon={Icon.Mail} size={20} color="primary" />
      <Icons icon={Icon.Settings} size={24} color="secondary" />
      <Icons icon={Icon.Trash} size={16} color="destructive" />
    </>
  );
}

Size System

Consistent sizing across all components:
import { Size } from 'nightwatch-ui';

// Available sizes
Size.X_SMALL  // Extra small
Size.SMALL    // Small
Size.MEDIUM   // Medium (default for most components)
Size.LARGE    // Large

Type System

Button and component variants:
import { Type } from 'nightwatch-ui';

Type.PRIMARY      // Primary action (filled)
Type.SECONDARY    // Secondary action (outlined)
Type.TERTIARY     // Tertiary action (ghost)
Type.DESTRUCTIVE  // Destructive action (red)

Accessibility

All components follow accessibility best practices:
  • Proper ARIA attributes
  • Keyboard navigation support
  • Focus management
  • Screen reader compatibility
  • Color contrast compliance

Best Practices

Use Type.DESTRUCTIVE for delete actions, Type.PRIMARY for main CTAs, and Type.SECONDARY for alternative actions.
Use forceTheme sparingly - let components inherit theme from context when possible.
Stick to the standard Size enum values for visual consistency across your application.
Always provide dataTest props for E2E testing and ensure keyboard accessibility.

TypeScript Support

Nightwatch UI is fully typed with TypeScript. Import types directly:
import type { 
  ButtonProps, 
  DialogProps, 
  InputProps,
  DropdownProps 
} from 'nightwatch-ui';

Dependencies

Core dependencies:
  • react ^17.0.2
  • react-dom ^17.0.2
  • styled-components ^5.3.3
  • @mui/material ^5.10.0
  • framer-motion ^6.3.3
  • @floating-ui/react-dom ^1.0.1
See libs/nightwatch-ui/package.json:40 for the complete dependency list.

Build docs developers (and LLMs) love