UNPKG

@makolabs/ripple

Version:

Simple Svelte 5 powered component library ✨

603 lines (492 loc) 15.8 kB
# 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 ```shell npm i @makolabs/ripple ``` ## Usage Import ripple UI components ```svelte <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` ```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: ```typescript // 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: ```svelte <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: ```svelte <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: ```svelte <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: ```svelte <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: ```svelte <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: ```svelte <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: ```svelte <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: ```svelte <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: ```svelte <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: ```svelte <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: ```svelte <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: ```svelte <script lang="ts"> import { Button, Modal, Card, Table, Select, Dropdown } from '@makolabs/ripple'; </script> ``` You can still import specific component types when needed: ```svelte <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>