@makolabs/ripple
TypeScript icon, indicating that this package has built-in type declarations

0.0.1-dev.82 • Public • Published

Ripple UI

A modern, standardized Svelte 5 component library designed for simplicity, consistency, and AI-friendly usage patterns.

Key Features

  • Standardized API with consistent prop naming and patterns across components
  • Enum-based properties for predictable component customization
  • Strong TypeScript support with comprehensive type definitions
  • Utility-first approach built with TailwindCSS
  • Accessible components adhering to modern web standards
  • Simplified component consumption ideal for both human and AI developers

Getting started

Install the project

npm i @makolabs/ripple

Usage

Import ripple UI components

<script lang="ts">
	import { Button, Card, Modal } from '@makolabs/ripple';
</script>

<div class="px-12 pt-12">
	<Card title="Hello World" color="warning">
		<p>This is a card component</p>
	</Card>
</div>

Paste the following CSS import code in app.css

@source "../node_modules/@makolabs/ripple";

@theme {
	/* Default (default) */
	--color-default-50: oklch(0.984 0.003 247.858);
	--color-default-100: oklch(0.96 0.006 247.858);
	--color-default-200: oklch(0.91 0.008 247.858);
	--color-default-300: oklch(0.85 0.01 247.858);
	--color-default-400: oklch(0.76 0.012 247.858);
	--color-default-500: oklch(0.65 0.015 247.858);
	--color-default-600: oklch(0.54 0.018 247.858);
	--color-default-700: oklch(0.45 0.015 247.858);
	--color-default-800: oklch(0.35 0.012 247.858);
	--color-default-900: oklch(0.25 0.01 247.858);
	--color-default-950: oklch(0.15 0.008 247.858);

	/* Primary (Blue) */
	--color-primary-50: oklch(0.97 0.025 250);
	--color-primary-100: oklch(0.94 0.035 250);
	--color-primary-200: oklch(0.89 0.055 250);
	--color-primary-300: oklch(0.82 0.075 250);
	--color-primary-400: oklch(0.74 0.095 250);
	--color-primary-500: oklch(0.65 0.115 250);
	--color-primary-600: oklch(0.55 0.125 250);
	--color-primary-700: oklch(0.45 0.115 250);
	--color-primary-800: oklch(0.35 0.095 250);
	--color-primary-900: oklch(0.25 0.075 250);
	--color-primary-950: oklch(0.15 0.055 250);

	/* Secondary (Slate) */
	--color-secondary-50: oklch(0.97 0.02 255);
	--color-secondary-100: oklch(0.94 0.03 255);
	--color-secondary-200: oklch(0.89 0.04 255);
	--color-secondary-300: oklch(0.82 0.05 255);
	--color-secondary-400: oklch(0.74 0.06 255);
	--color-secondary-500: oklch(0.65 0.07 255);
	--color-secondary-600: oklch(0.55 0.065 255);
	--color-secondary-700: oklch(0.45 0.055 255);
	--color-secondary-800: oklch(0.35 0.045 255);
	--color-secondary-900: oklch(0.25 0.035 255);
	--color-secondary-950: oklch(0.15 0.025 255);

	/* Info (Sky) */
	--color-info-50: oklch(0.97 0.025 220);
	--color-info-100: oklch(0.94 0.04 220);
	--color-info-200: oklch(0.89 0.06 220);
	--color-info-300: oklch(0.82 0.085 220);
	--color-info-400: oklch(0.74 0.105 220);
	--color-info-500: oklch(0.65 0.125 220);
	--color-info-600: oklch(0.55 0.115 220);
	--color-info-700: oklch(0.45 0.105 220);
	--color-info-800: oklch(0.35 0.085 220);
	--color-info-900: oklch(0.25 0.065 220);
	--color-info-950: oklch(0.15 0.045 220);

	/* Success (Green) */
	--color-success-50: oklch(0.97 0.025 145);
	--color-success-100: oklch(0.94 0.04 145);
	--color-success-200: oklch(0.89 0.06 145);
	--color-success-300: oklch(0.82 0.08 145);
	--color-success-400: oklch(0.74 0.1 145);
	--color-success-500: oklch(0.65 0.12 145);
	--color-success-600: oklch(0.55 0.11 145);
	--color-success-700: oklch(0.45 0.1 145);
	--color-success-800: oklch(0.35 0.08 145);
	--color-success-900: oklch(0.25 0.06 145);
	--color-success-950: oklch(0.15 0.04 145);

	/* Warning (Yellow) */
	--color-warning-50: oklch(0.97 0.025 90);
	--color-warning-100: oklch(0.94 0.045 90);
	--color-warning-200: oklch(0.89 0.065 90);
	--color-warning-300: oklch(0.82 0.085 90);
	--color-warning-400: oklch(0.74 0.105 90);
	--color-warning-500: oklch(0.65 0.125 90);
	--color-warning-600: oklch(0.55 0.115 90);
	--color-warning-700: oklch(0.45 0.105 90);
	--color-warning-800: oklch(0.35 0.085 90);
	--color-warning-900: oklch(0.25 0.065 90);
	--color-warning-950: oklch(0.15 0.045 90);

	/* Danger (Red) */
	--color-danger-50: oklch(0.97 0.025 25);
	--color-danger-100: oklch(0.94 0.045 25);
	--color-danger-200: oklch(0.89 0.065 25);
	--color-danger-300: oklch(0.82 0.085 25);
	--color-danger-400: oklch(0.74 0.105 25);
	--color-danger-500: oklch(0.65 0.125 25);
	--color-danger-600: oklch(0.55 0.115 25);
	--color-danger-700: oklch(0.45 0.105 25);
	--color-danger-800: oklch(0.35 0.085 25);
	--color-danger-900: oklch(0.25 0.065 25);
	--color-danger-950: oklch(0.15 0.045 25);
}

Design Philosophy

Ripple UI was built with a focus on consistency and standardization. Every component follows the same patterns for customization:

Standardized Enums

Components use standardized enums for colors, sizes, and variants:

// Colors available for most components
Color.DEFAULT    // 'default'
Color.PRIMARY    // 'primary'
Color.SECONDARY  // 'secondary'
Color.INFO       // 'info'
Color.SUCCESS    // 'success'
Color.WARNING    // 'warning'
Color.DANGER     // 'danger'

// Sizes available for most components
Size.XS    // 'xs'
Size.SM    // 'sm'
Size.BASE  // 'base'
Size.LG    // 'lg'
Size.XL    // 'xl'
Size.XXL   // '2xl'

Consistent Props Pattern

All components follow a consistent props pattern with predictable naming:

  • color: Component color theme (using the Color enum)
  • size: Component size (using the Size enum)
  • class: Custom CSS classes for the component
  • Event handlers with on prefix (e.g., onclick, onchange)
  • Element-specific class props named with component + 'class' (e.g., titleclass, bodyclass)

Component Variants

Most components in Ripple UI support variants to customize their appearance. Here are some examples:

Button Variants

Buttons come with different variants, colors, sizes, and shapes:

<script lang="ts">
	import { Button, Color, Size } from '@makolabs/ripple';
</script>

<!-- Different button variants -->
<Button variant="solid" color={Color.PRIMARY}>Solid Button</Button>
<Button variant="outline" color={Color.SECONDARY}>Outline Button</Button>
<Button variant="ghost" color={Color.DANGER}>Ghost Button</Button>
<Button variant="link" color={Color.INFO}>Link Button</Button>

<!-- Button with onclick handler -->
<Button 
  color={Color.SUCCESS}
  onclick={() => console.log('Button clicked')}
>
  Click Me
</Button>

<!-- Button as link -->
<Button 
  href="https://example.com" 
  target="_blank"
  color={Color.PRIMARY}
>
  Visit Website
</Button>

<!-- Button sizes -->
<Button size={Size.XS}>Extra Small</Button>
<Button size={Size.SM}>Small</Button>
<Button size={Size.BASE}>Base</Button>
<Button size={Size.LG}>Large</Button>
<Button size={Size.XL}>Extra Large</Button>
<Button size={Size.XXL}>2X Large</Button>

<!-- Button variants with different colors -->
<Button variant="solid" color={Color.PRIMARY}>Primary Solid</Button>
<Button variant="solid" color={Color.DANGER}>Danger Solid</Button>
<Button variant="outline" color={Color.SUCCESS}>Success Outline</Button>
<Button variant="ghost" color={Color.WARNING}>Warning Ghost</Button>
<Button variant="link" color={Color.INFO}>Info Link</Button>

Modal Variants

Modals with different sizes and custom content:

<script lang="ts">
	import { Modal, Button, Size } from '@makolabs/ripple';
	let isOpen = false;
</script>

<Button onclick={() => isOpen = true}>Open Modal</Button>

<!-- Basic modal -->
<Modal 
  open={isOpen} 
  title="Basic Modal"
  size={Size.BASE}
  onClose={() => isOpen = false}
>
  <p>Modal content goes here</p>
</Modal>

<!-- Modal with different size -->
<Modal 
  open={isOpen} 
  title="Large Modal"
  size={Size.XL}
  onClose={() => isOpen = false}
>
  <p>This modal is larger and provides more content space</p>
</Modal>

<!-- Modal with custom header and footer -->
<Modal 
  open={isOpen} 
  onClose={() => isOpen = false}
  size={Size.BASE}
>
  <svelte:fragment slot="header">
    <div class="flex items-center">
      <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 16 16">
        <path d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
      </svg>
      <h3 class="text-lg font-medium">Custom Header</h3>
    </div>
  </svelte:fragment>
  
  <p>Modal with custom header and footer</p>
  
  <svelte:fragment slot="footer">
    <div class="flex justify-end space-x-2">
      <Button variant="outline" onclick={() => isOpen = false}>Cancel</Button>
      <Button color={Color.PRIMARY}>Save Changes</Button>
    </div>
  </svelte:fragment>
</Modal>

<!-- TODO: Remove position prop from Modal component in future versions -->

Drawer Component

Drawers can slide in from different edges of the screen:

<script lang="ts">
	import { Drawer, Button } from '@makolabs/ripple';
	let isDrawerOpen = false;
</script>

<Button onclick={() => isDrawerOpen = true}>Open Drawer</Button>

<Drawer 
  open={isDrawerOpen} 
  position="right"
  onClose={() => isDrawerOpen = false}
>
  <div class="p-4">
    <h3 class="text-lg font-medium mb-4">Drawer Title</h3>
    <p class="mb-4">This is a drawer that slides in from the side of the screen.</p>
    <Button onclick={() => isDrawerOpen = false}>Close Drawer</Button>
  </div>
</Drawer>

PageHeader Component

A component for consistent page headers:

<script lang="ts">
	import { PageHeader, Button, Color } from '@makolabs/ripple';
	
	const breadcrumbs = [
	  { label: 'Dashboard', href: '#' },
	  { label: 'Projects', href: '#' },
	  { label: 'Current Project' }
	];
</script>

<PageHeader 
  title="Project Dashboard"
  description="View and manage your project details"
  breadcrumbs={breadcrumbs}
>
  <svelte:fragment slot="actions">
    <Button color={Color.PRIMARY}>New Project</Button>
  </svelte:fragment>
</PageHeader>

Card Variants

Cards can be customized with different styles:

<script lang="ts">
	import { Card, StatsCard, Color } from '@makolabs/ripple';
</script>

<Card title="Basic Card" color={Color.PRIMARY}>
  <p>Card content goes here</p>
</Card>

<StatsCard 
  label="Monthly Sales"
  value="$865,000"
  previousValue="$750,000"
  previousValuePrefix="vs"
  trend={15.3}
  color={Color.SUCCESS}
  chartData={[20, 25, 30, 22, 35, 40, 38, 45, 50]}
  icon={
    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 16 16">
      <path d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
    </svg>
  }
/>

Table Component

Tables for displaying structured data with pagination and sorting:

<script lang="ts">
	import { Table, Color, Size } from '@makolabs/ripple';
	
	let data = [
	  { id: 1, name: 'John Doe', email: 'john@example.com', status: 'Active' },
	  { id: 2, name: 'Jane Smith', email: 'jane@example.com', status: 'Inactive' },
	  { id: 3, name: 'Robert Johnson', email: 'robert@example.com', status: 'Active' }
	];
	
	const columns = [
	  { key: 'name', label: 'Name', sortable: true },
	  { key: 'email', label: 'Email', sortable: true },
	  { key: 'status', label: 'Status' }
	];
	
	let selected = [];
	let sort = { column: 'name', direction: 'asc' };
</script>

<Table 
  data={data} 
  {columns}
  color={Color.PRIMARY}
  size={Size.BASE}
  pageSize={10}
  selectable={true}
  bind:selected={selected}
  bind:sort={sort}
  striped={true}
/>

Tab Component

Tabs for organizing content into different views:

<script lang="ts">
	import { TabGroup, TabContent, Color, Size } from '@makolabs/ripple';
	
	const tabs = [
	  { value: 'overview', label: 'Overview' },
	  { value: 'details', label: 'Details' },
	  { value: 'settings', label: 'Settings' }
	];
	
	let activeTab = 'overview';
	
	function handleTabChange(value) {
	  console.log(`Tab changed to ${value}`);
	}
</script>

<TabGroup 
  tabs={tabs} 
  bind:selected={activeTab}
  onchange={handleTabChange}
  color={Color.PRIMARY}
  size={Size.BASE}
>
  <TabContent value="overview" persisted>
    <p>Overview content here</p>
  </TabContent>
  
  <TabContent value="details" persisted>
    <p>Details content here</p>
  </TabContent>
  
  <TabContent value="settings" persisted>
    <p>Settings content here</p>
  </TabContent>
</TabGroup>

Badge Component

Badges for displaying statuses and counts:

<script lang="ts">
	import { Badge, Color, Size } from '@makolabs/ripple';
</script>

<Badge color={Color.PRIMARY} size={Size.BASE}>New</Badge>
<Badge color={Color.SUCCESS}>Success</Badge>
<Badge color={Color.WARNING}>Warning</Badge>
<Badge color={Color.DANGER}>43</Badge>

Select Component

Dropdown selector for choosing from a list of options:

<script lang="ts">
	import { Select, Size } from '@makolabs/ripple';
	
	const items = [
	  { label: 'Option 1', value: 'option1' },
	  { label: 'Option 2', value: 'option2' },
	  { label: 'Option 3', value: 'option3', disabled: true },
	  { label: 'Option 4', value: 'option4' }
	];
	
	let selected = 'option1';
	
	function handleSelect(event) {
	  console.log('Selected:', event.value);
	}
</script>

<Select 
  {items} 
  bind:value={selected} 
  class="w-64"
  size={Size.BASE}
  onselect={handleSelect}
/>

Dropdown Component

Menu dropdown for actions and navigation:

<script lang="ts">
	import { Dropdown, Size } from '@makolabs/ripple';
	import FluentChevronDown16Filled from '$icons/FluentChevronDown16Filled.svelte';
	
	const sections = [
	  {
	    items: [
	      { 
	        label: 'Edit', 
	        icon: FluentPen16Filled,
	        onclick: () => console.log('Edit clicked') 
	      },
	      { 
	        label: 'Duplicate', 
	        icon: FluentPenSparkle24Filled,
	        onclick: () => console.log('Duplicate clicked') 
	      }
	    ]
	  },
	  {
	    items: [
	      { 
	        label: 'Delete', 
	        icon: FluentDelete24Filled,
	        onclick: () => console.log('Delete clicked') 
	      }
	    ]
	  }
	];
</script>

<Dropdown 
  sections={sections} 
  label="Actions"
  size={Size.BASE}
  icon={FluentChevronDown16Filled}
/>

Component Composition

Ripple UI components are designed to work together seamlessly:

<script lang="ts">
  import { Card, TabGroup, TabContent, Button, Color, Size } from '@makolabs/ripple';
  
  const tabs = [
    { value: 'overview', label: 'Overview' },
    { value: 'details', label: 'Details' },
    { value: 'settings', label: 'Settings' }
  ];
  
  let activeTab = 'overview';
</script>

<Card title="Project Information" color={Color.PRIMARY}>
  <TabGroup 
    tabs={tabs} 
    bind:selected={activeTab}
    color={Color.INFO}
    size={Size.BASE}
  >
    <TabContent value="overview">
      <p>Project overview content here...</p>
      <Button variant="solid" color={Color.SUCCESS} size={Size.SM}>
        Take Action
      </Button>
    </TabContent>
    
    <TabContent value="details">
      <p>Project details content here...</p>
    </TabContent>
    
    <TabContent value="settings">
      <p>Project settings content here...</p>
    </TabContent>
  </TabGroup>
</Card>

Latest Updates

Ripple UI now exports all components from a central entry point, making it easier to import components:

<script lang="ts">
	import { Button, Modal, Card, Table, Select, Dropdown } from '@makolabs/ripple';
</script>

You can still import specific component types when needed:

<script lang="ts">
	import { Button, type ButtonProps } from '@makolabs/ripple';
	
	// Create a custom button with specific props
	const myButton: ButtonProps = {
		variant: 'outline',
		color: 'primary',
		size: 'lg',
		rounded: 'xl'
	};
</script>

<Button {...myButton}>Custom Button</Button>

Package Sidebar

Install

npm i @makolabs/ripple

Weekly Downloads

304

Version

0.0.1-dev.82

License

none

Unpacked Size

382 kB

Total Files

140

Last publish

Collaborators

  • bishwasbh
  • sharktu