npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ronnydrori/jupiter-ui

v1.0.5

Published

Jupiter Design System - Plug and play UI components with Mantine and Emotion

Readme

Jupiter UI Design System

A comprehensive design system built with React, TypeScript, Mantine v7, and Storybook. This library provides a complete set of design tokens, themes, and reusable components for building consistent user interfaces.

🏗️ Architecture

Directory Structure

libs/jupiter-ui/
├── .storybook/          # Storybook configuration
│   ├── main.ts          # Storybook main config
│   ├── preview.tsx      # Global decorators & theme setup
│   └── preview.css      # Preview styling
├── src/
│   ├── design-system/
│   │   ├── tokens/      # Design tokens (spacing, typography, etc.)
│   │   ├── theme/       # Theme system & providers
│   │   ├── components/  # React components
│   │   ├── hooks/       # Custom React hooks
│   │   └── utils/       # Utility functions
│   └── index.ts         # Main export file
└── package.json

🛠️ Technology Choices

This design system was built with the following technologies, chosen for their balance of ease of use, customization, and team familiarity:

UI Foundation: Mantine v7

  • Why Mantine? Easy integration with 100+ pre-built components, flexible theme API with CSS variables, and 40+ included hooks. Provides good customization without fighting defaults, and excellent TypeScript support.
  • Benefits: Faster development with ready-to-use components and extensible theming.

CSS Strategy: Emotion

  • Why Emotion? Zero learning curve for our CSS-in-JS experienced team. Works seamlessly with Mantine, provides type-safe styling, dynamic theming capabilities, and co-located styles with components.
  • Benefits: Full JavaScript power for styling, theme access via props, and easy refactoring.

File Organization: Feature-Based Structure

  • Why Feature-Based? Components are self-contained in their own folders, making it easy to add, modify, or remove features without affecting others. Scales well for medium-sized component libraries.
  • Benefits: Intuitive organization, easy maintenance, and clear boundaries between components.

Icons: Lucide + Phosphor

  • Why These Libraries? Both provide excellent style control (size, color, stroke), MIT license, and tree-shaking support. Lucide for general UI icons, Phosphor for weight variants when needed.
  • Benefits: Consistent iconography, small bundle size (~200-250b per icon), and flexible styling.

Overall Stack Rationale

  • Balanced Approach: Prioritizes speed of development while maintaining design control and customization.
  • Team Fit: Leverages existing CSS-in-JS expertise to minimize learning curve.
  • Scalability: Feature-based organization supports growth from 20-40 components to more.
  • Modern Standards: TypeScript-first, accessible components, and responsive design.
  • Dynamic Theming: Supports runtime palette loading via environment variables for flexible theming (e.g., yanai.json, rotem.json).

� Design Tokens

All design tokens are defined in src/design-system/tokens/:

  • Spacing (spacing.ts) - Consistent spacing scale (0.25rem - 20rem)
  • Typography (typography.ts) - Font families, sizes, weights, line heights
  • Radius (radius.ts) - Border radius values (xs, sm, md, lg, xl)
  • Borders (borders.ts) - Border width values
  • Shadows (shadows.ts) - Box shadow definitions (xs, sm, md, lg, xl)
  • Breakpoints (breakpoints.ts) - Responsive breakpoints (xs, sm, md, lg, xl)

Example Token File: spacing.ts

import type { TokenScaleKey } from './types';

export const spacing = {
  0: '0px',
  1: '4px',
  2: '8px',
  3: '12px',
  4: '16px',
  5: '20px',
  6: '24px',
  7: '28px',
  8: '32px',
  9: '36px',
} as const;

export const semanticSpacing: Partial<Record<TokenScaleKey, string>> = {
  xs: spacing[1], // 4px
  sm: spacing[2], // 8px
  md: spacing[4], // 16px
  lg: spacing[6], // 24px
  xl: spacing[8], // 32px
} as const;

export type SpacingToken = keyof typeof spacing;
export type SemanticSpacingToken = keyof typeof semanticSpacing;

🎭 Theme System

Theme Structure

The theme system is located in src/design-system/theme/:

  • types.ts - TypeScript types for theme structure
  • theme.ts - Core theme creator using design tokens
  • mantine.ts - Mantine theme configuration
  • ThemeProvider.tsx - React context provider for theme management

Dynamic Palette Loading

The design system supports dynamic color palette loading at runtime:

  • usePalette Hook: Loads color palettes asynchronously from JSON files
  • Environment Variable: Set VITE_PROJECT_NAME to specify the project name (e.g., yanai or rotem)
  • Palette Path: Fetches from /color-palettes/${VITE_PROJECT_NAME}.json
  • Fallback: Defaults to defaultPalette.ts if no environment variable is set or loading fails
// Example: Loading a palette
const colors = usePalette(); // Loads from /color-palettes/${VITE_PROJECT_NAME}.json or defaults to defaultPalette

Using Themes

import { JupiterThemeProvider } from '@libraries/jupiter-ui';

function App() {
  return (
    <JupiterThemeProvider>
      {/* Your app */}
    </JupiterThemeProvider>
  );
}

Theme Context Hook

import { useThemeContext } from '@libraries/jupiter-ui';

function MyComponent() {
  const { mode, toggleMode, setMode } = useThemeContext();
  // mode: 'light' | 'dark'
  // toggleMode: () => void
  // setMode: (mode: ThemeMode) => void
}

🧩 Components

Component Guidelines

All components are located in src/design-system/components/[ComponentName]/:

  1. No index.ts files - Import directly from component files
  2. Component file: ComponentName.tsx
  3. Test file: ComponentName.test.tsx - REQUIRED (enforced by ESLint)
  4. Stories file: ComponentName.stories.tsx
  5. Export from main index: Update src/index.ts
  6. Set displayName on exported React components and higher-order components (e.g., ComponentName.displayName = 'ComponentName'; internal helper components don't need this)

⚠️ Testing Requirement: Every component must have a corresponding test file. This is enforced by ESLint's require-test-file rule and will prevent commits without tests.

Component Structure

// JpButton.tsx
import { Button as MantineButton } from '@mantine/core';
import type { ButtonProps as MantineButtonProps } from '@mantine/core';
import { buttonVariants, type JupiterVariant } from '../utils/variant-factory';

export interface JpButtonProps extends Omit<MantineButtonProps, 'variant'> {
  variant?: JupiterVariant;
}

export function JpButton({ variant = 'primary', ...props }: JpButtonProps) {
  return (
    <MantineButton
      variant={buttonVariants[variant] ?? buttonVariants.primary}
      {...props}
    />
  );
}

// Required: set the display name for better DX in React DevTools, Storybook, and logs
JpButton.displayName = 'JpButton';

Custom Styling with Emotion (Function Syntax)

Always use Emotion's function form for styled components, not tagged template strings. This ensures better type safety, prop filtering, and consistent patterns across the library.

import styled from '@emotion/styled';
// Optional: filter invalid DOM props when styling host elements
// import isPropValid from '@emotion/is-prop-valid';
import { JpButton, useThemeContext } from '@libraries/jupiter-ui';

// Create a styled button with custom styles (function approach)
const StyledButton = styled(JpButton, { label: 'styled-button' })(() => ({
  backgroundColor: 'hotpink',
  color: 'white',
  '&:hover': {
    backgroundColor: 'deeppink',
  },
  // Use static selectors to style inner elements
  '& .mantine-Button-label': {
    fontWeight: 'bold',
    textTransform: 'uppercase',
  },
}));

// Create a themed button using design tokens (function approach)
const ThemedButton = styled(JpButton, { label: 'themed-button' })(({ theme }) => ({
  backgroundColor: '#3b82f6',
  color: 'white',
  borderRadius: theme?.radius?.md || '0.5rem',
  padding: `${theme?.spacing?.md || '0.75rem'} ${theme?.spacing?.lg || '1rem'}`,
  fontWeight: theme?.typography?.fontWeights?.semibold || 600,
  '&:hover': {
    backgroundColor: '#2563eb',
    transform: 'translateY(-1px)',
    boxShadow: theme?.shadows?.md || '0 4px 6px rgba(0,0,0,0.1)',
  },
  // Style inner elements
  '& .mantine-Button-label': {
    fontFamily: theme?.typography?.fontFamilies?.body || 'system-ui',
  },
}));


function Demo() {
  return (
    <div>
      <StyledButton>Styled with Emotion</StyledButton>
      <ThemedButton>
        Themed Button
      </ThemedButton>
    </div>
  );
}

Key Points:

  • Use Emotion's function syntax: styled(Component, options)(props => styles)
  • Do not use tagged template strings (e.g., styled.div`..., styled(Button)`...)
  • Use Mantine's class selectors (e.g., .mantine-Button-label) to target inner elements
  • Access theme tokens via props.theme (pass theme from useThemeContext())
  • Use transient props ($propName) to avoid passing custom props to DOM
  • All Mantine component props still work on styled components
  • Styled components can be used in Storybook stories

Storybook Stories

// ComponentName.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { ComponentName } from './ComponentName';

const meta = {
  title: 'Components/ComponentName',
  component: ComponentName,
  parameters: {
    layout: 'centered',
  },
  tags: ['autodocs'],
  argTypes: {
    variant: {
      control: 'select',
      options: ['primary', 'secondary', 'outline', 'ghost'],
    },
  },
} satisfies Meta<typeof ComponentName>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Primary: Story = {
  args: {
    variant: 'primary',
    children: 'Component Text',
  },
};

📚 Storybook

Configuration

  • Version: v8.6.15
  • Framework: React + Vite
  • Addons:
    • storybook-addon-mantine - Mantine theme integration
    • Standard Storybook addons (essentials, interactions, etc.)

Running Storybook

npm run storybook

Storybook will be available at http://localhost:6006

🔧 Technology Stack

  • React v18.3.1
  • TypeScript v5.5.4 (strict mode)
  • Mantine v7.15.1 (UI component library)
  • Emotion v11.13.5 (CSS-in-JS styling)
  • Storybook v8.6.14
  • Vite v5.4.11 (build tool)
  • Vitest v3.2.4 (testing framework)
  • Lucide + Phosphor (icons)

📝 Coding Conventions

Imports

  • Use named exports (not default exports except for Storybook meta)
  • Import components directly: import { Button } from './components/Button/Button'
  • No intermediate index files in component folders

TypeScript

  • Use explicit types for all public APIs
  • Export interface definitions alongside components
  • Use type for type aliases, interface for object shapes
  • No any types allowed

File Naming

  • Components: PascalCase (e.g., Button.tsx)
  • Stories: ComponentName.stories.tsx
  • Utils/Tokens: camelCase (e.g., colors.ts)

Component Variants

Use semantic variant names that describe intent, not implementation:

  • primary, secondary, outline, ghost
  • filled, light, subtle (Mantine internal variants)

Map semantic variants to Mantine variants inside components.

🎯 Best Practices

Theme Provider

  • Always wrap components in JupiterThemeProvider when using useThemeContext()
  • ThemeProvider provides theme context and token access to all components

Mantine Integration

  • Extend Mantine components, don't replace them
  • Override Mantine's theme through createAppTheme() function
  • Use Mantine's utility functions (e.g., rem(), em())

State Management

  • Avoid useEffect for syncing props to state
  • Use render-time state updates: if (prop !== state) setState(prop)
  • This prevents cascading renders and React act() warnings

🚀 Development Workflow

  1. Add new tokens in src/design-system/tokens/
  2. Update theme in src/design-system/theme/ if needed
  3. Create component in src/design-system/components/[Name]/
  4. Add stories in same folder as component
  5. Export from main in src/index.ts
  6. Test in Storybook with npm run storybook
  7. Commit using conventional commits format

📦 Export Pattern

All exports go through src/index.ts:

// Tokens
export * from './design-system/tokens/spacing';
export * from './design-system/tokens/radius';
export * from './design-system/tokens/borders';
export * from './design-system/tokens/typography';
export * from './design-system/tokens/shadows';
export * from './design-system/tokens/breakpoints';

// Theme
export * from './design-system/theme/types';
export * from './design-system/theme/mantine';
export {
  JupiterThemeProvider,
  useThemeContext,
} from './design-system/theme/ThemeProvider';
export type { JupiterThemeProviderProps } from './design-system/theme/ThemeProvider';

// Utils
export {
  buttonVariants,
  inputVariants,
  selectVariants,
  checkboxVariants,
} from './design-system/utils/variant-factory';
export type { JupiterVariant } from './design-system/utils/variant-factory';

// Components
export { JpButton } from './design-system/components/Button/JpButton';
export type { JpButtonProps } from './design-system/components/Button/JpButton';

// Hooks
export * from './design-system/hooks/usePalette';

Scripts

# Development
npm run storybook          # Start Storybook on localhost:6006
npm run build-storybook    # Build static Storybook

# Linting
npm run lint              # Run ESLint
npm run lint:fix          # Fix ESLint issues

# Type Checking
npm run typecheck         # Run TypeScript compiler check

# Testing
npm run test              # Run unit tests with Vitest
npm run test-storybook    # Run storybook tests in the cli

📖 Official Documentation

  • Storybook Interaction Tests
    https://storybook.js.org/docs/writing-tests/interaction-testing

  • Storybook Vitest Addon
    https://storybook.js.org/docs/8/writing-tests/test-addon

🔒 ESLint Rules

This library enforces strict rules to maintain quality and plug-and-play architecture:

TypeScript Rules

  • @typescript-eslint/no-explicit-any: No any types allowed
  • @typescript-eslint/consistent-type-imports: Prefer type imports for better tree-shaking
  • @typescript-eslint/prefer-nullish-coalescing: Use ?? over || for null/undefined checks
  • @typescript-eslint/prefer-optional-chain: Use optional chaining (?.) instead of manual checks
  • ⚠️ @typescript-eslint/no-non-null-assertion: Warn on non-null assertions (!)

Import Restrictions

  • ❌ No imports from other monorepo libraries (@libraries/* except @libraries/jupiter-ui)
  • ❌ No imports from apps (apps/*)
  • ❌ No imports from other libs (libs/*)
  • ❌ No relative imports outside src/ directory
  • ✅ Only allowed external dependencies: @mantine/*, @emotion/*, @phosphor-icons/react, lucide-react, vitest, react, react-dom, @testing-library/*, @storybook/*, @types/*

Component Requirements

  • ✅ Every component must have a corresponding .test.tsx file (enforced by test-file/require-test-file rule)
  • ✅ Set displayName on exported React components for better DX in DevTools and logs

React Rules

  • ✅ All React recommended rules
  • ✅ React Hooks recommended rules
  • ✅ JSX runtime rules

Other

  • ✅ Fully type-safe
  • ✅ Plug-and-play architecture

🤝 Contributing

When adding components:

  1. Create component folder in src/design-system/components/[Name]/
  2. Create Name.tsx with component implementation
  3. Create Name.stories.tsx with comprehensive stories
  4. Export component and types from src/index.ts
  5. Ensure no external monorepo dependencies
  6. Follow semantic variant naming
  7. Add proper TypeScript types

📖 Additional Resources