JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 66
  • Score
    100M100P100Q53280F
  • License MIT

LSTech UI component library

Package Exports

  • @lsts_tech/ui
  • @lsts_tech/ui/styles.css

Readme

@lsts_tech/ui - Internal UI Kit for LSTS

This is the internal, white-label, brand-agnostic UI component library built for LSTS Solutions and its associated brands.

While it is primarily designed for our internal products, it is built to be enterprise-ready and brand-agnostic, making it suitable for use in other enterprise applications that require a robust, composable, and themeable design system.

Note: The source code for this library is closed source and part of the private LSTS repository. The package is published to npm for internal consumption and distribution.

Features

  • Brand-Agnostic Architecture: Switch between brands (LSTS, TLÁO) with a single prop
  • Full Theming Support: Light/dark mode with CSS custom properties
  • Design Tokens: HSL-based color system for easy customization
  • Accessible: WCAG compliant with proper focus states and ARIA attributes
  • Type-Safe: Full TypeScript support with proper type exports
  • Composable: Built with Radix UI primitives for flexibility

Installation

pnpm add @lsts_tech/ui

Setup

1. Wrap your app with providers

import { BrandProvider, ThemeProvider } from "@lsts_tech/ui";

function App({ children }) {
  return (
    <BrandProvider defaultBrand="lsts">
      <ThemeProvider defaultTheme="system">
        {children}
      </ThemeProvider>
    </BrandProvider>
  );
}

2. Import the global styles

import "@lsts_tech/ui/styles.css";
// or in your layout.tsx
import "@lsts_tech/ui";

3. Configure Tailwind

In your tailwind.config.ts:

import { createRequire } from "module";
const require = createRequire(import.meta.url);

export default {
  darkMode: ["class", "data-theme='dark'"],
  content: [
    // ... your content
    "./node_modules/@lsts_tech/ui/dist/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {
      // The UI library provides all theme tokens
    },
  },
  plugins: [require("tailwindcss-animate")],
};

Brand System

Supported Brands

  • LSTS (default): LSTech Solutions - Blue/purple theme
  • TLÁO: Warm earth tones - Orange/green theme

Switching Brands

import { useBrand, BrandSwitcher } from "@lsts_tech/ui";

// Programmatic switching
function MyComponent() {
  const { brand, setBrand } = useBrand();
  
  return (
    <button onClick={() => setBrand("tlao")}>
      Switch to TLÁO
    </button>
  );
}

// UI Component
function Settings() {
  return <BrandSwitcher variant="cards" showDescription />;
}

Adding a New Brand

To add a new brand (e.g., "acme"):

  1. Define brand colors in your global CSS:
[data-brand="acme"] {
  --brand-primary-hue: 200;
  --brand-primary-sat: 80%;
  --brand-primary-light: 50%;
  
  --brand-secondary-hue: 280;
  --brand-secondary-sat: 60%;
  --brand-secondary-light: 40%;
  
  --brand-accent-hue: 45;
  --brand-accent-sat: 90%;
  --brand-accent-light: 60%;
}
  1. Extend the Brand type in your app:
type Brand = "lsts" | "tlao" | "acme";
  1. Add brand option to the switcher:
const brandOptions = [
  { value: "lsts", label: "LSTS", icon: <Building2 /> },
  { value: "tlao", label: "TLÁO", icon: <Landmark /> },
  { value: "acme", label: "Acme", icon: <Rocket /> },
];

<BrandSwitcher options={brandOptions} />

Theme System

Light/Dark Mode

import { useTheme, ThemeSwitcher } from "@lsts_tech/ui";

function Header() {
  const { theme, setTheme, resolvedTheme } = useTheme();
  
  return (
    <div>
      <span>Current: {resolvedTheme}</span>
      <ThemeSwitcher variant="segmented" />
    </div>
  );
}

Design Tokens

The UI system uses CSS custom properties for all design tokens:

/* Colors */
--primary: hsl(var(--primary));
--primary-foreground: hsl(var(--primary-foreground));
--secondary: hsl(var(--secondary));
--background: hsl(var(--background));
--foreground: hsl(var(--foreground));

/* Brand Colors */
--brand-primary: hsl(var(--brand-primary-hue) var(--brand-primary-sat) var(--brand-primary-light));
--brand-secondary: var(--brand-secondary);
--brand-accent: var(--brand-accent);
--brand-gradient: linear-gradient(...);

/* Elevation */
--shadow-elevation-1: ...;
--shadow-elevation-2: ...;
--shadow-elevation-3: ...;
--shadow-elevation-4: ...;

/* Radius */
--radius: 0.5rem;
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);

/* Spacing */
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-3: 0.75rem;
--space-4: 1rem;
/* ... */

Components

Button

import { Button } from "@lsts_tech/ui";

// Variants
<Button>Default</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="link">Link</Button>

// Brand variants
<Button variant="brand">Brand Primary</Button>
<Button variant="brand-secondary">Brand Secondary</Button>
<Button variant="brand-ghost">Brand Ghost</Button>

// Sizes
<Button size="xs">Extra Small</Button>
<Button size="sm">Small</Button>
<Button size="default">Default</Button>
<Button size="lg">Large</Button>
<Button size="xl">Extra Large</Button>
<Button size="2xl">2X Large</Button>

// Icon buttons
<Button size="icon" variant="outline">
  <Settings className="h-4 w-4" />
</Button>

// Loading state
<Button loading>Loading...</Button>

// With icons
<Button leftIcon={<Mail />} leftIcon={<ArrowRight />}>
  Send Email
</Button>

Card

import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "@lsts_tech/ui";

<Card variant="elevated" radius="lg">
  <CardHeader>
    <CardTitle>Card Title</CardTitle>
    <CardDescription>Card description here</CardDescription>
  </CardHeader>
  <CardContent>
    Content goes here
  </CardContent>
  <CardFooter>
    <Button>Action</Button>
  </CardFooter>
</Card>

Input

import { Input } from "@lsts_tech/ui";

// Basic
<Input placeholder="Enter text..." />

// With icons
<Input 
  leftIcon={<Search />} 
  rightIcon={<Keyboard />}
  placeholder="Search..." 
/>

// States
<Input state="error" placeholder="Error state" />
<Input state="success" placeholder="Success state" />

// Variants
<Input variant="filled" placeholder="Filled variant" />
<Input variant="ghost" placeholder="Ghost variant" />
<Input variant="brand" placeholder="Brand variant" />

Badge

import { Badge } from "@lsts_tech/ui";

// Variants
<Badge>Default</Badge>
<Badge variant="secondary">Secondary</Badge>
<Badge variant="outline">Outline</Badge>
<Badge variant="ghost">Ghost</Badge>
<Badge variant="brand">Brand</Badge>
<Badge variant="success">Success</Badge>
<Badge variant="warning">Warning</Badge>
<Badge variant="error">Error</Badge>

// Sizes
<Badge size="sm">Small</Badge>
<Badge size="default">Default</Badge>
<Badge size="lg">Large</Badge>
<Badge size="xl">Extra Large</Badge>

Alert

import { Alert } from "@lsts_tech/ui";

// Variants
<Alert title="Success!" variant="success">
  Your changes have been saved.
</Alert>

<Alert title="Warning" variant="warning">
  Please review your settings.
</Alert>

<Alert title="Error" variant="error">
  Something went wrong.
</Alert>

// Dismissible
<Alert 
  title="Info" 
  variant="info" 
  dismissible 
  onDismiss={() => console.log("Dismissed")}
>
  This alert can be dismissed.
</Alert>

// Brand variant
<Alert title="Welcome" variant="brand">
  Welcome to LSTS!
</Alert>

Brand Switcher

import { BrandSwitcher } from "@lsts_tech/ui";

// Button variant
<BrandSwitcher variant="buttons" size="md" />

// Card variant
<BrandSwitcher variant="cards" showDescription />

// Select variant
<BrandSwitcher variant="select" />

// Custom options
<BrandSwitcher
  options={[
    { value: "lsts", label: "LSTS", icon: <Building2 />, description: "LSTech Solutions" },
    { value: "tlao", label: "TLÁO", icon: <Landmark />, description: "TLÁO Project" },
  ]}
/>

Theme Switcher

import { ThemeSwitcher } from "@lsts_tech/ui";

// Segmented control (default)
<ThemeSwitcher variant="segmented" />

// Button variant
<ThemeSwitcher variant="buttons" />

// Card variant
<ThemeSwitcher variant="cards" showLabel />

// Select variant
<ThemeSwitcher variant="select" />

Customization

Customizing Design Tokens

Override CSS variables in your global CSS:

:root {
  /* Override radius */
  --radius: 0.75rem;
  
  /* Override shadows */
  --shadow-elevation-1: 0 2px 4px rgba(0, 0, 0, 0.1);
  
  /* Override spacing */
  --space-4: 1.25rem;
}

[data-theme="dark"] {
  /* Dark mode overrides */
  --shadow-elevation-1: 0 2px 4px rgba(0, 0, 0, 0.3);
}

Using Class Variance Authority (CVA)

All components support CVA variants for advanced customization:

import { buttonVariants } from "@lsts_tech/ui";

// Use variants without the component
<button className={buttonVariants({ variant: "brand", size: "lg" })}>
  Custom Button
</button>

API Reference

Providers

BrandProvider

Prop Type Default Description
defaultBrand Brand "lsts" Initial brand value
brands Brand[] ["lsts", "tlao"] Available brands
storageKey string "ui-brand" localStorage key

ThemeProvider

Prop Type Default Description
defaultTheme Theme "system" Initial theme value
storageKey string "ui-theme" localStorage key
enableSystem boolean true Enable system preference
disableTransitionOnChange boolean false Disable transitions on change

Hooks

useBrand

const { brand, setBrand, brands } = useBrand();

useTheme

const { theme, setTheme, resolvedTheme } = useTheme();

Browser Support

  • Chrome 88+
  • Firefox 78+
  • Safari 14+
  • Edge 88+

License

MIT © LSTech Solutions